Reputation: 36650
In the code below the getValue
method on the DataBankService
class is losing type information (see example). Is it possible keeping the type information?
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
Upvotes: 1
Views: 630
Reputation: 31
I would suggest creating a type for inferring the generic type of DataObject:
type DataObjectTypeInference<T> = T extends DataObject<infer U> ? U : never;
And then use it like that:
public getValue<K extends keyof DataBankProperties>(property: K) {
return this.dataBank[property].value as DataObjectTypeInference<
DataBankProperties[T]
>;
}
First, you define a type which looks after the generic type of DataObject of the current type instance. I personally prefer not to couple between property names and like the comment before me. I like it to be inferred by TypeScript, so if you decide to change value key to another name, it won't affect the type inference.
After that, you make a type assertion of the variable to be DataBankProperties[T] which gives you the type of DataObject and now you use the type we built and infer the generic type of DataObject.
Upvotes: 0
Reputation: 4020
You can do this with an assertion on the return type - using your generic as the key for DataBankProperties. I've renamed it K for convention (as in K for key)
public getValue<K extends keyof DataBankProperties>(property: K): DataBankProperties[K]['value'] {
return this.dataBank[property].value;
}
Upvotes: 1