TypeScript - может ли общее ограничение предоставлять "разрешенные" типы?
Учитывая следующий код...
type Indexable<TKey, TValue> = { [index: TKey]: TValue }
Это приводит к следующей ошибке:
Тип параметра подписи индекса должен быть "строка" или "число".
Есть ли способ ограничить TKey
быть "строкой" или "числом"?
2 ответа
Как Titian Cernicova-Dragomir, вы не можете использовать TKey
как тип в сигнатуре индекса, даже если он эквивалентен string
или же number
,
Если вы знаете, что TKey
это точно string
или же number
Вы можете просто использовать его напрямую и не указывать TKey
в вашем типе:
type StringIndexable<TValue> = { [index: string]: TValue }
type NumberIndexable<TValue> = { [index: number]: TValue }
В сторону: Оказывается, что в куче мест в TypeScript индексный тип для свойств должен быть string
, и не number
, В таких местах number
обычно рассматривается как своего рода подтип string
, Это потому, что в JavaScript индексы преобразуются в string
в любом случае, когда вы их используете, это приводит к такому поведению:
const a = { 0: "hello" };
console.log(a[0]); // outputs "hello"
console.log(a['0']) // *still* outputs "hello"
Так как вы не можете использовать number
в дальнейшем я буду игнорировать это; если вам нужно использовать цифровые клавиши, TypeScript, вероятно, позволит вам, или вы можете преобразовать в string
вручную. Вернуться к остальной части ответа:
Если вы хотите разрешить TKey
быть более конкретным, чем string
, то есть разрешены только определенные ключи, вы можете использовать сопоставленные типы:
type Indexable<TKey extends string, TValue> = { [K in TKey]: TValue }
Вы бы использовали его, передавая строковый литерал или объединение строковых литералов для TKey
:
type NumNames = 'zero' | 'one' | 'two';
const nums: Indexable<NumNames, number> = { zero: 0, one: 1, two: 2 };
type NumNumerals = '0' | '1' | '2';
const numerals: Indexable<NumNumerals, number> = {0: 0, 1: 1, 2: 2};
И если вы не хотите ограничивать ключ определенными литералами или объединениями литералов, вы все равно можете использовать string
как TKey
:
const anyNums: Indexable<string, number> = { uno: 1, zwei: 2, trois: 3 };
На самом деле это определение для Indexable<TKey, TValue>
настолько полезен, он уже существует в стандартной библиотеке TypeScript как Record<K,T>
:
type NumNames = 'zero' | 'one' | 'two';
const nums: Record<NumNames, number> = { zero: 0, one: 1, two: 2 };
Поэтому я рекомендую вам использовать Record<K,T>
для этих целей, поскольку это стандарт, и другие разработчики TypeScript, которые читают ваш код, скорее всего, знакомы с ним.
Надеюсь, это поможет; удачи!
Вы можете ограничить использование TKey из строки или числа (используя extends), но это не удовлетворит компилятор. index
должен быть числом или строкой, а не универсальным типом или любым другим типом в этом отношении. Это задокументировано в спецификации языка