Различать записи и объекты в TypeScript?

#typescript

#typescript

Вопрос:

Я пытаюсь создать условный тип в TypeScript, который различает типы «записей» (где ключи неограниченные / динамические) и простые типы объектов (где ключи предварительно определены).

 type R = { [x: string]: any }
type O = { a: string; b: number }
 

Я могу получить ключи типов и проверить наличие некоторого, надеюсь, не существующего свойства:

 type R = { [x: string]: any }
type O = { a: string; b: number }

type IsRecord<T> = '__NEVER__' extends keyof T ? true : false

type RR = IsRecord<R> // true
type OO = IsRecord<O> // false
 

Но это кажется хакерским. Возможно ли это более чистым способом?

Комментарии:

1. Является ли троичный в '__NEVER__' extends keyof R ? true : false необходимым? Если '__NEVER__' extends keyof R возвращает a boolean , это избыточно.

2. В качестве альтернативы вы могли бы использовать !!('__NEVER__' extends keyof O)

3. @AlexH в случае условных типов я считаю, что это необходимо. Но это был только псевдокод, я отредактировал пример, чтобы сделать его немного более понятным, надеюсь. Я ищу ответ, который не связан __NEVER__ с тестом поддельного ключа.

Ответ №1:

string Индексируемый тип можно определить, проверив, является ли string он одним из его ключей:

 type IsRecord<T> = string extends keyof T ? true : false

type RR = IsRecord<R> // true
type OO = IsRecord<O> // false
 

Да, вы можете сделать это так, как вы делали с поддельным ключом, но если вы беспокоитесь о «хакерстве», я бы предложил использовать string вместо этого.

Обратите внимание, что существуют также number индексируемые типы (например Array<any> ), и поскольку string это не обязательно ключ number индексируемого типа, они будут отображаться как не-запись:

 type N = { [x: number]: any };
type NN = IsRecord<N>; // false
 

Если вам нужно идентифицировать оба string и number индексы как похожие на записи, достаточно проверить number , поскольку, с точки зрения ключей, number присваивается string . (Это может сбить с толку, но поскольку все объекты JS фактически используют string symbol ключи or , всякий раз, когда вы индексируете объект с помощью a number , он принудительно преобразуется в string первый. arr[0] и arr["0"] это одно и то же. TypeScript поддерживает это, рассматривая все string индексируемые типы как имеющие оба string и number в качестве своих ключей. Приведенная выше ссылка проходит через это):

 type IsNumericIndex<T> = number extends keyof T ? true : false

type RRR = IsNumericIndex<R> // true
type OOO = IsNumericIndex<O> // false
type NNN = IsNumericIndex<N> // true
 

Игровая площадка ссылка на код

Комментарии:

1. Вау, так просто, но я об этом не подумал. Спасибо!