В TypeScript, как указать только ключи универсального объекта, значения которого являются строками? - PullRequest
0 голосов
/ 04 февраля 2019

Я пытался создать тип, который состоит из ключей типа T, значения которых являются строками.В псевдокоде это будет keyof T where T[P] is a string.

Единственный способ, которым я могу думать об этом, состоит в двух шагах:

// a mapped type that filters out properties that aren't strings via a conditional type
type StringValueKeys<T> = { [P in keyof T]: T[P] extends string ? T[P] : never };

// all keys of the above type
type Key<T> = keyof StringValueKeys<T>;

Однако компилятор TS говорит, что Key<T> просторавный keyof T, хотя я отфильтровал ключи, значения которых не являются строками, установив для них never, используя условный тип.

Так что он по-прежнему допускает это, например:

interface Thing {
    id: string;
    price: number;
    other: { stuff: boolean };
}

const key: Key<Thing> = 'other';

, когда единственным допустимым значением key действительно должно быть "id", а не "id" | "price" | "other", поскольку значения двух других ключей не являются строками.

Ссылкак примеру кода на площадке TypeScript

1 Ответ

0 голосов
/ 04 февраля 2019

Это можно сделать с помощью условных типов и типов поиска , например:

type KeysMatching<T, V> = {[K in keyof T]: T[K] extends V ? K : never}[keyof T];

, а затем вы извлекаете ключи, свойства которых соответствуют string вот так:

const key: KeysMatching<Thing, string> = 'other'; // ERROR!
// '"other"' is not assignable to type '"id"'

Подробно:

KeysMatching<Thing, string> ➡

{[K in keyof Thing]: Thing[K] extends string ? K : never}[keyof Thing] ➡

{ 
  id: string extends string ? 'id' : never; 
  price: number extends string ? 'number' : never;
  other: { stuff: boolean } extends string ? 'other' : never;
}['id'|'price'|'other'] ➡

{ id: 'id', price: never, other: never }['id' | 'price' | 'other'] ➡

'id' | never | never ➡

'id'

Обратите внимание, что то, что вы делали:

type SetNonStringToNever<T> = { [P in keyof T]: T[P] extends string ? T[P] : never };

действительно просто превращалось встроковое свойство значения в never значения свойства.Это не касалось клавиш.Ваш Thing станет {id: string, price: never, other: never}.И ключи этого такие же, как ключи Thing.Основное отличие от этого и KeysMatching состоит в том, что вы должны выбирать ключи, а не значения (поэтому P и не T[P]).

Надеюсь, это поможет.Удачи!

...