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 должен быть числом или строкой, а не универсальным типом или любым другим типом в этом отношении. Это задокументировано в спецификации языка

Другие вопросы по тегам