#typescript #typescript-typings
#typescript #typescript-typings
Вопрос:
Извините за запутанный заголовок. Вот моя проблема.
У меня есть тип:
type Person = {
firstName?: string;
middleName?: string;
lastName?: string;
}
Допустим, я хочу получить один из этих атрибутов динамически.
const getPersonAttribute = (field: string) => {
return person[field];
}
Это выдает ошибки TS (что-то о неявном типе <string>
или <any>
не может быть индексом типа Person
), поэтому, чтобы заставить его работать, я делаю это:
const getPersonAttribute = (field: 'firstName' | 'middleName' | 'lastName') => {
return person[field];
}
Есть ли лучший способ сделать это? Каким-то образом динамически получать свойства типа?
Комментарии:
1. нравится
keyof Person
?2. Да. Именно так. Как отмечалось в других ответах, похоже, что для смешанных типов все немного по-другому. getPersonAttribute<K расширяет ключ Person>(поле: K) {}
Ответ №1:
Вы просто хотите keyof
, как описано здесь:
declare const person: Person;
const getPersonAttribute = (field: keyof Person) => {
return person[field]; // okay
}
// const getPersonAttribute: (field: keyof Person) => string | undefined
В этом случае возвращаемый тип getPersonAttribute
is string | undefined
. Если у вас были разные типы свойств в Person
(скажем, age
типа number
),
type Person = {
firstName?: string;
middleName?: string;
lastName?: string;
age?: number;
}
тогда возвращаемым типом будет объединение string
, number
, и undefined
, что может быть недостаточно конкретным для ваших вариантов использования:
// const getPersonAttribute: (field: keyof Person) => string | number | undefined
const personLastName = getPersonAttribute("lastName"); // string | number | undefined
const personAge = getPersonAttribute("age"); // string | number | undefined
Если вы хотите getPersonAttribute()
, чтобы тип вывода зависел от конкретной клавиши ввода, вам нужно сделать функцию универсальной в типе клавиши ввода:
const getPersonAttributeGen = <K extends keyof Person>(field: K) => {
return person[field]; // okay
}
// const getPersonAttributeGen:
// <K extends "firstName" | "middleName" | "lastName" | "age">(field: K) => Person[K]
Это приводит к выводу типа Person[K]
, типа поиска (задокументировано в той же ссылке выше):
const personLastNameBetter = getPersonAttributeGen("lastName"); // string | undefined
const personAgeBetter = getPersonAttributeGen("age"); // number | undefined
Комментарии:
1. Идеальный. Спасибо! Дополнительная информация и объяснение очень ценятся!
2. Но почему Typescript работает таким образом? Я не вижу разницы между этими двумя или как в последней реализации мы даем больше подсказок для typescript. это просто то, как Typescript решил работать, или за этим стоит веская причина?
3. «Я не вижу разницы между этими двумя» Они аннотируются по-разному.
keyof Person
является типом объединения. Еслиfield
имеет типkeyof Person
, тоperson[field]
имеет типPerson[keyof Person]
, другой тип объединения. Объединение в, объединение. Если вам нужно что-то более конкретное, чем это, вы аннотируете как общее.<K extends keyof Person>
значениеfield
типаK
K
потенциально уже , чемkeyof Person
, а затемperson[field]
имеет типPerson[K]
, который потенциально уже, чемPerson[keyof Person]
, в зависимости от того, чтоK
предполагается при вызове fn .4. «Это просто то, как TypeScript решил работать, или за этим стоит веская причина?» Я не уверен, о чем вы здесь спрашиваете; как вы думаете, если я просто напишу
field => person[field]
, компилятор должен вывести общую подпись? Это интересно и было предложено (я думаю, что это так), но нет четкого способа сделать это без зависания компилятора. Вы могли бы задать то же самое для условных типов (еще одна функция), но, как упоминалось в этом выпуске , это плохая идея:5. Обычно вы не хотите, чтобы каждая деталь реализации функции отражалась в сигнатуре ее вызова. Подписи вызовов являются контрактами, а детали реализации намеренно скрыты. Если я скажу
const greet = (x: string) => "Hello, " x "!"
, что, вероятно, хочу, чтобы компилятор сказал, что это функция fromstring
tostring
, а не то, что она специально добавляется"Hello"
к строке. Если я решу изменить"Hello"
"Greetings"
, это деталь реализации, а не изменение подписи функции. Если я хочу что-то более конкретное, что можно выразить в системе типов, я могу аннотировать.
Ответ №2:
С keyof
помощью вы можете получить ключи определенного типа. Вот синтаксис, который я бы использовал (обратите внимание, что я выбрал функцию вместо функции со стрелкой, чтобы сделать ее более читаемой):
function getPersonAttribute<K extends keyof Person>(field: K) {
return person[field];
}
Комментарии:
1. Спасибо! Я только отметил другой ответ как принятый ответ, потому что парень подробно рассказал. Но это то, что я искал. Спасибо!
2. Я понимаю! В любом случае рад помочь
![]()