Я столкнулся с проблемой, которая в основном заключается в сужении типа, возвращаемого объектом, доступ к которому осуществляется по его ключу. Допустим, у меня есть объект, значения которого могут быть number
или string
:
type Value = number | string;
enum DatumKey {
numberDatum = 'numberDatum',
stringDatum = 'stringDatum',
}
class DemoClass {
private data = {
[DatumKey.numberDatum]: 1337,
[DatumKey.stringDatum]: 'foobar',
}
public getValue(key: DatumKey): Value {
return this.data[key];
}
public doSomething(): void {
const num: number = this.getValue(DatumKey.numberDatum); // TS expects `number | string`, I expect `number`
const str: string = this.getValue(DatumKey.stringDatum); // TS expects `number | string`, I expect `string`
console.log({ num, str });
}
}
const dc = new DemoClass();
dc.doSomething();
См. Пример игровой площадки Typescript .
Failed подход
Итак, чтобы сузить тип, я попытался использовать обобщенный тип c, и тогда я могу сказать getValue<...>()
, какой тип я ожидаю взамен:
public getValue<T extends Value>(key: DatumKey): T {
// Error here: Type 'Value' not assignable to type 'T'
return this.data[key];
}
public doSomething(): void {
const num: number = this.getValue<number>(DatumKey.numberDatum); // TS expects `number`, it's good!
const str: string = this.getValue<string>(DatumKey.stringDatum); // TS expects `string`, it's good!
}
Однако при этом я получаю сообщение об ошибке TypeScript:
Type 'Value' is not assignable to type 'T'.
'Value' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.
Type 'string' is not assignable to type 'T'.
'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.
См. Пример игровой площадки Typescript .
Рабочий подход
Затем я натолкнулся на аналогичный вопрос, который предложил использовать перегрузку функций для решения проблемы . Оно делает. Однако я не уверен, почему перегрузка функции решает проблему:
public getValue<T extends Value>(key: DatumKey): T
public getValue(key: DatumKey): Value {
return this.data[key];
}
public doSomething(): void {
const num = this.getValue<number>(DatumKey.numberDatum); // TS expects `number`, it's good!
const str = this.getValue<string>(DatumKey.stringDatum); // TS expects `string`, it's good!
}
См. Пример игровой площадки Typescript .