Это можно сделать с помощью условных типов и типов поиска , например:
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]
).
Надеюсь, это поможет.Удачи!