Как я могу указать, что Typescript generic T [K] имеет числовой тип?

#typescript #generics #keyof

#typescript #общие сведения #keyof

Вопрос:

У меня есть простая функция Typescript, подобная этой:

 function getProperty<T, K extends keyof T>(obj: T, key: K): number {
  return obj[key]; // This line is not compiling.
  // Typescript will yell: "Type 'T[K]' is not assignable to type 'number'."
}
  

Мое использование выглядит следующим образом:

 const someObj = {
  myValue: 123,
  otherProperty: '321'
}

getProperty(someObj, 'myValue')
  

Я не буду знать, какой будет структура someObj .

Мой вопрос: как я могу указать, что T[K] является типом number статически?

Комментарии:

1. @T.J.Crowder Я обновил свой вариант использования, спасибо!

Ответ №1:

Если объекты, с которыми вы работаете, достаточно типизированы, чтобы их форма была известна typescript, вы вполне можете сделать это [ссылка на typescript playgrounds, если хотите немного поэкспериментировать].

Вам просто нужно сузить, какие идентификаторы key могут быть, до одного из этих ссылочных number свойств типа:

 const someObj = {
  myValue: 123,
  otherProperty: '321'
};

getProperty(someObj, 'myValue'); //       works
getProperty(someObj, 'otherProperty'); // wails

function getProperty<T, K extends NumericProps<T>>(obj: T, key: K): number {
    return obj[key];
}

type NumericProps<T> = {
    [K in keyof T]: T[K] extends number ? K : never
}[keyof T];
  

Ответ №2:

Я не буду знать, какой будет структура someObj .

Тогда TypeScript не сможет вам в этом помочь. Проверка типа TypeScript выполняется во время компиляции. Если структура someObj будет известна только во время выполнения, TypeScript не сможет обеспечить доступ к этой структуре с сохранением типов. Вам нужно было бы знать, каковы ключи свойств и возможные значения для этих свойств во время компиляции.

Например: В вашем примере имена свойств являются строками, а значения свойств — либо строками, либо числами (но не логическими значениями или объектами и т.д.). Вы можете объявить тип, индексируемый строками (поскольку все имена свойств в конечном счете являются строками или символами, в данном случае строками), где значениями свойств являются числа или строки:

 declare type SomeObjType = {
    [key: string]: number | string
};
  

и тогда getProperty это:

 function getProperty<T extends SomeObjType>(obj: T, key: string): number | string {
  return obj[key];
}
  

и вы можете использовать это следующим образом (в данном случае я использую JSON.parse для имитации получения этих данных из-за пределов программы):

 const someObj: SomeObjType = JSON.parse(`{
  "myValue": 123,
  "otherProperty": "321"
}`);

console.log(getProperty(someObj, 'myValue'));
console.log(getProperty(someObj, 'otherProperty'));
  

<a rel="noreferrer noopener nofollow" href="https:///www.typescriptlang.org/play/index.html#src=declare type SomeObjType = {
[key: string]: number | string
};

function getProperty(obj: T, key: string): number | string {
return obj[key];
}

const someObj: SomeObjType = JSON.parse(`{
«myValue»: 123,
«otherProperty»: «321»
}`);

console.log(getProperty(someObj, ‘myValue’));
console.log(getProperty(someObj, ‘otherProperty’));» rel=»nofollow noreferrer»>На игровой площадке

Но это мало что дает и исключает возможность того, что значения свойств являются чем-то иным, чем числами или строками.

Возможно, вам потребуется просто использовать object :

 function getProperty(obj: object, key: string) {
  return obj[key];
}

const someObj = JSON.parse(`{
  "myValue": 123,
  "otherProperty": "321"
}`);

console.log(getProperty(someObj, 'myValue'));
console.log(getProperty(someObj, 'otherProperty'));
  

На игровой площадке

Комментарии:

1. Вау, спасибо за подробное объяснение, T extends SomeObjType подходит для моего варианта использования! Спасибо!