如何在 TypeScript 中不存储的情况下进行内联类型检查?
How can I do inline type checking without storage in TypeScript?
我有一些接口
ITestInterface {
foo: string;
}
我想将此接口的实例作为参数传递给函数。该函数将采用任何对象类型,因此它不会自行进行类型检查。为了确保对象的类型正确,我可以使用存储:
const passMe: ITestInterface = { foo: "bar" };
someFunction(passMe);
但我希望有一种方法可以内联创建参数,同时仍然进行类型检查。
// made up example syntax
someFunction({ foo: "bar" } istype ITestInterface);
有没有像上面的内联示例那样的好方法?
我试过用as,但是不限制类型。例如,以下是有效的。
someFunction({ foo: "bar", hello: true } as ITestInterface);
在这种情况下我可以做的另一件事是修改 someFunction
以使用模板,但这不是我认为的好解决方案。我不会一直有这个特权。
someFunction<TYPE>(arg: TYPE) {
// modify function definition
}
someFunction<ITestInterface>({foo: "bar"});
您正在寻找的特定功能,例如 "type annotations for arbitrary expressions",在 TypeScript 中不存在。有一个开放的 suggestion 当前标记为 "needs proposal",因此如果它们引人注目并且与现有内容不同,您可能想要给它一个或描述您的想法。但在我看来并没有人在做这件事,所以如果我是你,我不会屏住呼吸。
这里有几种方法,每种方法都有自己的问题。
如您所见,最简单的方法是使用 type assertion。这可以防止您传入 完全不相关的 类型:
// assertion
someFunction({ foo: "bar" } as ITestInterface); // okay as expected
someFunction({ unrelatedThing: 1 } as ITestInterface); // error as expected
它还允许额外的属性(这仍然是可靠的和类型安全的,ITestInterface
类型的对象不保证不具有其他属性...它可能会让您感到惊讶,因为您期望 excess property checking,但这些只会在某个时候发生):
someFunction({ foo: "bar", hello: true } as ITestInterface); // okay by design,
// excess properties are allowed
但这里的大问题是类型断言让您不安全地缩小类型,因此以下不会是错误:
someFunction({} as ITestInterface); // no error ?! assertions also NARROW types
另一种方法是创建一个名为 isType
的辅助函数,如下所示:
// helper function
const isType = <T>(x: T) => x;
这几乎完全符合您的要求:
someFunction(isType<ITestInterface>({ foo: "bar" })); // okay as expected
someFunction(isType<ITestInterface>({ unrelatedThing: 1 })); // error as expected
someFunction(isType<ITestInterface>({ foo: "bar", hello: true })); // error as you want
someFunction(isType<ITestInterface>({})); // error as everyone wants
但是,正如您所说,这对您来说可能不值得。大多数运行时引擎会愉快地内联函数,例如 x => x
,所以我认为这不是 性能 问题。但这可能是一个优雅的问题,这取决于你。
无论如何,这些都是我能做的最好的了。希望有所帮助。祝你好运!
首先,接口应该由 class 实现。接口和 classes 都不能用于 TypeScript 中的 type-checking 简单对象 - 只需使用类型即可。另外,接口名称前的I
代表接口,所以应该写ITest
而不是ITestInterface
:
// replace this:
ITestInterface { foo: string }
// by this:
type Test = { foo: string }
现在让我们抛开这些software-development级别的言论,直击问题的核心:
如果你想确保 someFunction
总是调用类型 Type
的对象,编写如下函数定义就足够了,因为 TypeScript 会检测你代码中任何被调用的地方否则:
// like this
const someFunction: (arg: Type) => any = (arg) => { /*...*/ }
// or like this
function someFunction(arg: Type): any { /*...*/ }
如果您有一些论据表明您 知道 是 Type
类型,但不知何故 TS 编译器无法推断出这一点,这就是您使用 as
关键字。
someFunction({foo: 10}); // error
someFunction({foo: 'bar'});
someFunction({foo: Math.random()<1 ? 'bar' : 10}); // error
someFunction({foo: Math.random()<1 ? 'bar' : 10} as Type);
这基本上就是您在 compile-time 制作节目 type-safe 所需要做的全部工作。在 TypeScript playground.
查看上面的代码
如果出于某种原因,您想要增加一层额外的可靠性并确保您的程序在运行时 type-safe,则必须在运行时进行类型检查。这会导致性能开销,但如果您只是想确保一个对象上有几个属性,那只能在函数定义中占用一行:
const someSafeFunction(arg: Type): any {
if (Object.keys(arg).sort().join(',')!='propertyName1,propertyName2') throw new Error('Invalid argument type');
/* ... */
}
我有一些接口
ITestInterface {
foo: string;
}
我想将此接口的实例作为参数传递给函数。该函数将采用任何对象类型,因此它不会自行进行类型检查。为了确保对象的类型正确,我可以使用存储:
const passMe: ITestInterface = { foo: "bar" };
someFunction(passMe);
但我希望有一种方法可以内联创建参数,同时仍然进行类型检查。
// made up example syntax
someFunction({ foo: "bar" } istype ITestInterface);
有没有像上面的内联示例那样的好方法?
我试过用as,但是不限制类型。例如,以下是有效的。
someFunction({ foo: "bar", hello: true } as ITestInterface);
在这种情况下我可以做的另一件事是修改 someFunction
以使用模板,但这不是我认为的好解决方案。我不会一直有这个特权。
someFunction<TYPE>(arg: TYPE) {
// modify function definition
}
someFunction<ITestInterface>({foo: "bar"});
您正在寻找的特定功能,例如 "type annotations for arbitrary expressions",在 TypeScript 中不存在。有一个开放的 suggestion 当前标记为 "needs proposal",因此如果它们引人注目并且与现有内容不同,您可能想要给它一个或描述您的想法。但在我看来并没有人在做这件事,所以如果我是你,我不会屏住呼吸。
这里有几种方法,每种方法都有自己的问题。
如您所见,最简单的方法是使用 type assertion。这可以防止您传入 完全不相关的 类型:
// assertion
someFunction({ foo: "bar" } as ITestInterface); // okay as expected
someFunction({ unrelatedThing: 1 } as ITestInterface); // error as expected
它还允许额外的属性(这仍然是可靠的和类型安全的,ITestInterface
类型的对象不保证不具有其他属性...它可能会让您感到惊讶,因为您期望 excess property checking,但这些只会在某个时候发生):
someFunction({ foo: "bar", hello: true } as ITestInterface); // okay by design,
// excess properties are allowed
但这里的大问题是类型断言让您不安全地缩小类型,因此以下不会是错误:
someFunction({} as ITestInterface); // no error ?! assertions also NARROW types
另一种方法是创建一个名为 isType
的辅助函数,如下所示:
// helper function
const isType = <T>(x: T) => x;
这几乎完全符合您的要求:
someFunction(isType<ITestInterface>({ foo: "bar" })); // okay as expected
someFunction(isType<ITestInterface>({ unrelatedThing: 1 })); // error as expected
someFunction(isType<ITestInterface>({ foo: "bar", hello: true })); // error as you want
someFunction(isType<ITestInterface>({})); // error as everyone wants
但是,正如您所说,这对您来说可能不值得。大多数运行时引擎会愉快地内联函数,例如 x => x
,所以我认为这不是 性能 问题。但这可能是一个优雅的问题,这取决于你。
无论如何,这些都是我能做的最好的了。希望有所帮助。祝你好运!
首先,接口应该由 class 实现。接口和 classes 都不能用于 TypeScript 中的 type-checking 简单对象 - 只需使用类型即可。另外,接口名称前的I
代表接口,所以应该写ITest
而不是ITestInterface
:
// replace this:
ITestInterface { foo: string }
// by this:
type Test = { foo: string }
现在让我们抛开这些software-development级别的言论,直击问题的核心:
如果你想确保 someFunction
总是调用类型 Type
的对象,编写如下函数定义就足够了,因为 TypeScript 会检测你代码中任何被调用的地方否则:
// like this
const someFunction: (arg: Type) => any = (arg) => { /*...*/ }
// or like this
function someFunction(arg: Type): any { /*...*/ }
如果您有一些论据表明您 知道 是 Type
类型,但不知何故 TS 编译器无法推断出这一点,这就是您使用 as
关键字。
someFunction({foo: 10}); // error
someFunction({foo: 'bar'});
someFunction({foo: Math.random()<1 ? 'bar' : 10}); // error
someFunction({foo: Math.random()<1 ? 'bar' : 10} as Type);
这基本上就是您在 compile-time 制作节目 type-safe 所需要做的全部工作。在 TypeScript playground.
查看上面的代码如果出于某种原因,您想要增加一层额外的可靠性并确保您的程序在运行时 type-safe,则必须在运行时进行类型检查。这会导致性能开销,但如果您只是想确保一个对象上有几个属性,那只能在函数定义中占用一行:
const someSafeFunction(arg: Type): any {
if (Object.keys(arg).sort().join(',')!='propertyName1,propertyName2') throw new Error('Invalid argument type');
/* ... */
}