如何在没有存储在TypeScript中的情况下进行内联类型检查?

时间:2019-07-11 17:07:09

标签: typescript typechecking

我有一些界面

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"});

2 个答案:

答案 0 :(得分:1)

您要查找的特定功能(例如“任意表达式的类型注释”)在TypeScript中不存在。我只是在GitHub中搜索了一个现有功能请求,但似乎找不到一个。要么不存在,要么我的搜索能力不够强。

这里有几种方法,每种方法都有自己的问题。


如您所见,最简单的方法是使用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之类的内联函数,因此我认为这不是 performance 问题。但这可能是一个优雅的问题,这取决于您。


无论如何,这些是我能做到的最好的。希望能有所帮助。祝你好运!

Link to code

答案 1 :(得分:1)

首先,接口应该由类实现。接口和类均不得用于TypeScript中的简单对象的类型检查-只需使用类型即可。另外,接口名称前的I代表接口,因此您应该写ITest而不是ITestInterface

// replace this:
ITestInterface { foo: string }
// by this:
type Test = { foo: string }

现在,我们将这些软件开发级别的说明放在一边,并攻击问题的核心:

如果要确保someFunction始终调用类型为Type的对象,则按如下所示编写函数定义就足够了,因为TypeScript会检测到代码中被其他地方调用的任何地方:

// like this
const someFunction: (arg: Type) => any = (arg) => { /*...*/ }
// or like this
function someFunction(arg: Type): any { /*...*/ }

如果您有一个知道类型为Type的参数,但是TS编译器无法以某种方式推断出来,那就可以使用as关键字。 / p>

someFunction({foo: 10}); // error
someFunction({foo: 'bar'});
someFunction({foo: Math.random()<1 ? 'bar' : 10}); // error
someFunction({foo: Math.random()<1 ? 'bar' : 10} as Type);

基本上,这就是在编译时使程序类型安全的所有您需要做的。在TypeScript playground上查看上面的代码。


如果出于某种原因想要增加一层可靠性,并确保程序在运行时是类型安全的,则必须在运行时键入check。这可能会导致性能开销,但是如果您只想确保某个对象上有一些属性,那么该属性只能在函数定义中占据一行:

const someSafeFunction(arg: Type): any {
  if (Object.keys(arg).sort().join(',')!='propertyName1,propertyName2') throw new Error('Invalid argument type');
  /* ... */
}