仅具有泛型类型参数的泛型 TypeScript 类型保护
Generic TypeScript type guards with only generic type parameter
我想在 TypeScript 中构建一个具有以下签名的通用类型保护:
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)
这样我就可以再次加入了。
你不能在 typescript 中编写一个完全通用的类型保护,因为在运行时没有类型信息。在运行时,您的函数无法了解有关 T 的任何信息。请参阅 typescript playground.
中如何转译您的函数代码
每当 T 到达 JS 时,这意味着您使用 T 作为值,而不是类型,并且您会收到您提到的错误:'T' only refers to a type, but is being used as a value here.
,
并且生成的 JS 代码不是类型安全的。
让我们看看柯里化会发生什么。我们从您提供的两个参数函数开始,但我将交换参数,以获取类型保护
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
通过指定第一个参数来限制类型。
Link 去游乐场 here
所以即使使用柯里化,您也无法实现您的 func
。
我想在 TypeScript 中构建一个具有以下签名的通用类型保护:
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)
这样我就可以再次加入了。
你不能在 typescript 中编写一个完全通用的类型保护,因为在运行时没有类型信息。在运行时,您的函数无法了解有关 T 的任何信息。请参阅 typescript playground.
中如何转译您的函数代码每当 T 到达 JS 时,这意味着您使用 T 作为值,而不是类型,并且您会收到您提到的错误:'T' only refers to a type, but is being used as a value here.
,
并且生成的 JS 代码不是类型安全的。
让我们看看柯里化会发生什么。我们从您提供的两个参数函数开始,但我将交换参数,以获取类型保护
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
通过指定第一个参数来限制类型。 Link 去游乐场 here
所以即使使用柯里化,您也无法实现您的 func
。