我想在打字稿中使用以下签名构建通用的typeGuard:
declare function typeGuard<T>(obj: any): o is T;
我找到了一些文章(例如this),这些文章用这样的签名解决了:
declare function typeGuard<T>(obj: any, clazz: T): o is T;
但这需要一个人通过代码携带类型信息,因此首选第一个签名。 第二种情况下的解决方案如下:
type typeMap = {
string: string;
number: number;
boolean: boolean;
}
type PrimitiveOrConstructor =
| string
| { new (...args: any[]): any }
| keyof typeMap;
type GuardedType<T extends PrimitiveOrConstructor> = T extends { new(...args: any[]): infer U; } ? U : T extends keyof typeMap ? typeMap[T] : never;
function typeGuard<T extends PrimitiveOrConstructor>(o: any, className: T): o is GuardedType<T> {
const localPrimitiveOrConstructor: PrimitiveOrConstructor = className;
if (typeof localPrimitiveOrConstructor === 'string') {
return typeof o === localPrimitiveOrConstructor;
}
return o == localPrimitiveOrConstructor || (typeof localPrimitiveOrConstructor === 'object' && o instanceof localPrimitiveOrConstructor);
}
这将导致:
typeGuard(2, 'number'); // true
typeGuard('foobar', 'string'); // true
typeGuard(new Foobar(), Foobar); // true
但是如果我在通用类型上下文中怎么办这样的功能:
declare function <T>func(arg: T);
在这种情况下,不可能做
function <T>func(arg: any) {
if (typeGuard(arg, T)) { ... } // throws error: T is used as a value
所以在这种情况下,我会喜欢这样的东西:
function <T>func(arg: any) {
if (typeGuard<T>(arg)) { ... }
我最近阅读了this文章,并试图提出这样的内容(当然不起作用):
type Check<X, Y> = X extends Y ? true : false;
declare function check<C, T>(o: T): Check<T, C>;
然后像这样使用它:
function <T>func(arg: any) {
if (check<T>(arg)) { ... }
PS:可以肯定的是,我不想将包装函数的签名更改为类似function <T>func(arg: any, clazz: T)
之类的东西,以便我可以再次欣赏。
答案 0 :(得分:0)
您不能在Typescript中编写完全通用的类型保护,因为在运行时没有类型信息。在运行时,您的函数对T一无所知。请参阅如何在typescript playground中编译函数的代码。
只要T到达JS,就意味着您将T用作值,而不是类型,并且得到了您提到的错误:'T' only refers to a type, but is being used as a value here.
,
生成的JS代码类型不安全。
让我们看看使用curring会发生什么。我们从您提供的两个参数函数开始,但是我将交换参数以进入类型警卫
function typeGuard<T extends PrimitiveOrConstructor>(className: T, o: any): o is GuardedType<T>
此功能的最新版本为:
function curriedTypeGuard<T extends PrimitiveOrConstructor>(className: T): (o: any) => o is GuardedType<T>
这就像是用于更专业的单参数类型防护的工厂。
const stringSpecializedTypeGuard = curriedTypeGuard("string") // (o: any) => o is string
const classSpecializedTypeGuard = curriedTypeGuard(Class); // (o: any) => o is Class
通过指定第一个参数来限制类型。 链接到游乐场here
因此,即使使用curry也无法实现func
。