TypeScript中如何实例化仅接受原始类型的泛型类型/接口
问题原因说明
- 第一种简单泛型实现返回字面量类型,是因为TypeScript默认会将传入的字面量参数的泛型参数推导为字面量类型,而非
string/number/boolean这类宽原始类型,导致后续同类型普通值无法赋值。 - 条件类型写法的报错是TypeScript的类型系统限制:当条件类型依赖未被具体值实例化的泛型参数时,编译器不会提前推导条件类型的最终结果,所以无法确认
T和AP<T>的类型兼容性,哪怕逻辑上二者是完全对应的。
可行实现方案
方案1:函数重载实现(最优雅,无类型断言)
通过函数重载分别限定三种原始类型的入参和返回值类型,实现签名只需兼容所有重载即可,完全不需要类型断言:
type Primitive = number | string | boolean; interface MyInterface<T extends Primitive> { value: T; } // 重载签名 function createMyInterface(value: string): MyInterface<string>; function createMyInterface(value: number): MyInterface<number>; function createMyInterface(value: boolean): MyInterface<boolean>; // 实现签名 function createMyInterface(value: Primitive): MyInterface<Primitive> { return { value }; } // 测试代码 const x = createMyInterface("someString"); // 类型为 MyInterface<string> x.value = "foo"; // 可正常赋值,无报错 const y = createMyInterface(42); // 类型为 MyInterface<number> const z = createMyInterface(true); // 类型为 MyInterface<boolean>
如果需要单独的createPrimitive函数,也可以用同样的重载写法:
function createPrimitive(value: string): string; function createPrimitive(value: number): number; function createPrimitive(value: boolean): boolean; function createPrimitive(value: Primitive) { return value; } let a = createPrimitive("someString"); // 类型为 string a = "foo"; // 可正常赋值
方案2:条件类型+安全类型断言
如果你不想写多份重载,可以保留你现有的条件类型定义,仅在返回值处做逻辑可证的安全类型断言,不需要断言为any:
type Primitive = number | string | boolean; type AP<T extends Primitive> = T extends string ? string : T extends number ? number : boolean; interface MyInterface<T extends Primitive> { value: T; } function createMyInterface<T extends Primitive>(value: T): MyInterface<AP<T>> { return { value: value as AP<T> }; } function createPrimitive<T extends Primitive>(v: T): AP<T> { return v as AP<T>; }
该方案的类型安全性和重载方案一致,因为你可以完全确定T和AP<T>的类型是兼容的。
内容的提问来源于stack exchange,提问作者htho
相关产品推荐
相关产品推荐

