#typescript #typescript-typings #typescript-generics
#typescript #typescript-типизации #typescript-дженерики
Вопрос:
В приведенном ниже коде getValue
метод в DataBankService
классе теряет информацию о типе (см. Пример). Возможно ли сохранить информацию о типе?
https://stackblitz.com/edit/typescript-rx-playground-gqnzuq?file=index.ts
import { Subject } from "rxjs/";
export interface DataObject<T> {
value: T;
observable: Subject<T>;
}
export class DataBankService {
private dataBank: DataBankProperties;
constructor() {
this.dataBank = new DataBankProperties();
}
public getValue<T extends keyof DataBankProperties, V>(property: T) {
return this.dataBank[property].value;
}
}
class DataBankProperties {
money: DataObject<number> = this.createDataObject();
token: DataObject<string> = this.createDataObject();
testament: DataObject<object> = this.createDataObject();
private createDataObject<T>() {
return {
value: null,
observable: new Subject<T>(),
};
}
}
const dataBank = new DataBankService();
// token is now type >> token: string | number | object
const token = dataBank.getValue('token');
// I would like this to be type string
// token is now type >> token: string | number | object
const money = dataBank.getValue('money');
// I would like this to be type number
Ответ №1:
Вы можете сделать это с помощью утверждения для возвращаемого типа — используя свой generic в качестве ключа для DataBankProperties. Я переименовал его в K для соглашения (как в K для ключа)
public getValue<K extends keyof DataBankProperties>(property: K): DataBankProperties[K]['value'] {
return this.dataBank[property].value;
}
Комментарии:
1. Теперь тип — DataObject<число>/DataObject<строка> вместо number / строка
2. Вы использовали его как DataObject, а не как примитив
3. Большое вам спасибо, вы, кажется, довольно хороши в TS. Есть ли у вас какие-либо предложения по хорошим ресурсам TS?
4. Просто выберите тему и начните над ней работать! Чтобы узнать, как работает этот конкретный шаблон — я нашел эту документацию TS действительно полезной — если вы можете разобраться в том, как работает пример выбора, это в значительной степени то, что делается здесь: typescriptlang.org/docs/handbook /…
5. Как только вы освоитесь с этим, следующим уровнем, вероятно, будут условные типы — мне потребовалось много времени, чтобы понять, как их использовать, но один из хороших примеров приведен здесь. artsy.github.io/blog/2018/11/21/conditional-types-in-typescript . Наконец, пару лет назад я делал презентацию о расширенных TS — некоторые материалы здесь могут быть полезны для вас в качестве своего рода дорожной карты (игнорируйте контекст react, в первой половине презентации есть несколько примеров) github.com/m-b-davis/typescript-hocs-context/blob/master /…
Ответ №2:
Я бы предложил создать тип для вывода общего типа DataObject:
type DataObjectTypeInference<T> = T extends DataObject<infer U> ? U : never;
А затем используйте это так:
public getValue<K extends keyof DataBankProperties>(property: K) {
return this.dataBank[property].value as DataObjectTypeInference<
DataBankProperties[T]
>;
}
Сначала вы определяете тип, который соответствует универсальному типу DataObject текущего экземпляра type. Я лично предпочитаю не связывать имена свойств и ставить лайк комментарию передо мной. Мне нравится, чтобы это выводилось с помощью TypeScript, поэтому, если вы решите изменить значение key на другое имя, это не повлияет на вывод типа.
После этого вы делаете утверждение типа переменной как DataBankProperties[T], которое дает вам тип DataObject, и теперь вы используете тип, который мы создали, и выводите универсальный тип DataObject.