Могу ли я в Typescript ограничить тип значения с помощью универсального ключа?

#javascript #typescript #generics

#javascript #typescript #общие

Вопрос:

Как я могу в Typescript ограничить поле типа a для универсального типа определенным типом? Другими словами, я хочу написать функцию, которая принимает объект obj и ключ key этого объекта, но выдает ошибку Typescript, если obj[key] не имеет типа X . Например, если X string :

 function foo<T extends {}, K extends keyof T ???>(obj: T, key: K) { 
  console.log(obj[key])
}

>> foo({ fox: 'Mulder' }, 'fox')
Mulder

>> foo({ fox: 22 }, 'fox')
<some error here>
 

Ответ №1:

Ответ Геррика демонстрирует, как параметры типа обычно ограничены: 1) определите T ограничение как объект, затем 2) определите K ограничение как ключ T .

Однако для случая OP я бы изменил эту логику: 1) определите Key constraint как ключ общего свойства (он же string | number | symbol ), затем 2) Obj определите constraint как объект, содержащий свойство Key :

 function foo<Key extends PropertyKey, Obj extends Record<Key, string>>(obj: Obj, key: Key) {
    console.log(obj[key]);
}

foo({ fox: 'Mulder' }, 'fox'); // works fine

foo({ fox: 22 }, 'fox'); // Error (number is not assignable to string)
 

Попробуй это сделать.

Таким образом, вы получаете ошибки чтения (в этом контексте «число не может быть присвоено строке» гораздо менее запутанно, чем «строка не может быть присвоена никогда»), а также реализация функции ( console.log(obj[key]) бит) не требует никакого приведения к типу, поскольку obj она правильно набрана, чтобы иметь Key ключ.

Ответ №2:

Вы могли бы сделать так:

 function foo<T extends {}, K extends keyof T>(obj: T, key: T[K] extends string ? T[K] : never) { 
  console.log((obj as any)[key]);
}

foo({ fox: 'Mulder' }, 'fox'); // OK


foo({ fox: 22 }, 'fox'); // Argument of type 'string' is not assignable to parameter of type 'never'
 

Это подразумевает приведение obj as any в реализации, потому never что не может использоваться в качестве индексного типа, даже если ничто не может быть присвоено never , поэтому вызов функции с фактическим key параметром, который бы key разрешал тип never , никогда не будет компилироваться (по определению), но TypeScript недостаточно умен, чтобы сделать такой вывод.

Игровая площадка TypeScript