我找不到答案为什么通用函数中的 switch
不会在分支内强制转换我的类型。
我的代码是:
interface Id { id: number; }
enum Kind { square = "square", circle = "circle" }
interface Circle { kind: Kind.circle; radius: number; }
interface Square { kind: Kind.square; size: number; }
type Data = Circle | Square;
type ShapeModel<TData> = Id & TData;
class UnreachableError extends Error { public constructor(guard: never) { super(`Unsupported kind: ${JSON.stringify(guard)}`); } }
function myFunctionGeneric<TData extends Data>(data: TData): ShapeModel<TData> {
switch (data.kind) {
case Kind.circle:
return { ...data, id: 1 };
case Kind.square:
return { ...data, id: 2 };
default:
throw new UnreachableError(data); // <-- UNEXPECTED
// Argument of type 'TData' is not assignable to parameter of type 'never'.
// Type 'Data' is not assignable to type 'never'.
// Type 'Circle' is not assignable to type 'never'.
// ts(2345)
}
}
const myCircleData: Circle = { kind: Kind.circle, radius: 42 };
const mySquareData: Square = { kind: Kind.square, size: 42 };
// I want this. I'm passing Circle and want receive ShapeModel<Circle>
const myCircleModel: ShapeModel<Circle> = myFunctionGeneric(myCircleData);
// I want this. I'm passing Square and want receive ShapeModel<Square>
const mySquareModel: ShapeModel<Square> = myFunctionGeneric(mySquareData);
此方法无需通用 TData
即可正常工作。
有人可以解释一下,为什么 TypeScript 无法决定分支中的类型吗?
最佳答案
要缩小类型,它需要是联合,这里没有联合,它是泛型类型参数TData
。当然,这扩大了联盟。但 TS 不会试图缩小这一范围。如果你仔细想想,缩小范围是相当困难的,TData
可以是 Circle
的子类型,所以你不能缩小到 Circle
在 switch 情况下,它将是某种条件类型。
最简单的解决方案是使用泛型进行公共(public)签名,并使用可以通过 typescript 缩小范围的更简单联合的私有(private)签名。
function myFunctionGeneric<TData extends Data>(data: TData): ShapeModel<TData>
function myFunctionGeneric(data: Data): ShapeModel<Data> {
switch (data.kind) {
case Kind.circle:
return { ...data, id: 1 };
case Kind.square:
return { ...data, id: 2 };
default:
throw new UnreachableError(data);
}
}
关于TypeScript 高级类型 - 泛型函数内出现无法访问错误的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60339173/