一個函式接受一個可以是 TypeA 或 TypeB 的物件。第一個引數指定物件的型別。回傳的型別也取決于第一個引數。
問題:TypeScript 不會在 case(或 if)內推斷物件的型別。以下代碼有錯誤。
我知道我可以通過明確型別(add as TypeA)來解決這個問題,但我想知道是否有其他解決方案可以避免必須進行顯式強制轉換。
type TypeA = {
input: { foo: string }
output: { foo: number }
}
type TypeB = {
input: { bar: string }
output: { bar: number }
}
type Types = {
TypeA: TypeA
TypeB: TypeB
}
type TypesName = keyof Types
/**
* Takes TypeA or TypeB objects, and the return type is based on the type.
*/
function transform<N extends TypesName>(typeName: N, typeValue: Types[N]['input']): Types[N]['output'] {
switch (typeName) {
case 'TypeA':
return transformTypeA(typeValue) // Argument of type '{ foo: string; } | { bar: string; }' is not assignable to parameter of type '{ foo: string; }'.
case 'TypeB':
return transformTypeB(typeValue) // Argument of type '{ foo: string; } | { bar: string; }' is not assignable to parameter of type '{ bar: string; }'.
}
throw new Error('Unknown type')
}
function transformTypeA(typeA: TypeA['input']): TypeA['output'] {
return { foo: parseInt(typeA.foo) }
}
function transformTypeB(typeB: TypeB['input']): TypeB['output'] {
return { bar: parseInt(typeB.bar) }
}
const transformedValue = transform('TypeA', { foo: 'lol' })
console.log(transformedValue) // transformedValue is of type { foo: number }
uj5u.com熱心網友回復:
有一種方法,但現在可能不值得。
type Union<N extends keyof Types = keyof Types> = N extends N ? [typeName: N, typeValue: Types[N]['input']]: never;
function transform<N extends TypesName>(...p: Union<N>): Types[N]['output'] {
switch (p[0]) {
case 'TypeA':
return transformTypeA(p[1])
case 'TypeB':
return transformTypeB(p[1])
}
}
游樂場鏈接
我們需要使用索引訪問而不是定義單獨的引數或解構引數,因為這是打字稿看到兩個值相關的唯一方式。這可能會在以后的日子這里描述的支持這里
特別是,不支持將判別屬性和有效負載屬性解構為兩個區域變數并期望兩者之間存在耦合的模式,因為控制流分析器沒有“看到”連接。例如:
type Data = { kind: 'str', payload: string } | { kind: 'num', payload: number };
function foo({ kind, payload }: Data) {
if (kind === 'str') {
payload.length; // Error, payload not narrowed to string
}
}
我們以后可能會支持這種模式,但可能不會在這個 PR 中。
uj5u.com熱心網友回復:
在 TS 4.6 中可以使用Wishlist:支持相關聯合型別 #30581
type TypeA = {
input: { foo: string }
output: { foo: number }
}
type TypeB = {
input: { bar: string }
output: { bar: number }
}
type Types = {
TypeA: TypeA
TypeB: TypeB
}
type TypeMap = {
TypeA: TypeA,
TypeB: TypeB
};
type TypeMapAsGeneric<K extends keyof TypeMap = keyof TypeMap> = { [P in K]: TypeMap[P] }[K];
function transformTypeA(typeA: TypeA['input']): TypeA['output'] {
return { foo: parseInt(typeA.foo) }
}
function transformTypeB(typeB: TypeB['input']): TypeB['output'] {
return { bar: parseInt(typeB.bar) }
}
const transformers: { [K in keyof TypeMap]: (data: TypeMapAsGeneric<K>['input']) => TypeMapAsGeneric<K>['output'] } = {
TypeA: transformTypeA,
TypeB: transformTypeB
};
const transform = <K extends keyof TypeMap>(typeName: K, inputValue: TypeMapAsGeneric<K>['input']): TypeMapAsGeneric<K>['output'] => {
const transformer = transformers[typeName];
return transformer(inputValue);
}
const transformedValue = transform('TypeA', { foo: '123' })
console.log(transformedValue)
游樂場鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/407779.html
標籤:
下一篇:可能包含給定介面物件的物件
