为什么类型谓词的类型必须可以赋值给它的参数类型?

时间:2018-05-19 01:35:40

标签: typescript

我有一个类型谓词:

// tslint:disable-next-line:no-any
const isString = (value: any): value is string {
  return typeof value === 'string'
}

这有效,但它要求我禁用我的linter。我宁愿这样做:

const isString = <T>(value: T): value is string {
  return typeof value === 'string'
}

这种类型不是any,而是每种类型都有1个typeguard函数,即每a -> Boolean个函数a

打字稿抱怨说:

  

类型谓词的类型必须可分配给其参数的类型。     类型'string'不能分配给'T'类型。

这对我来说没有意义......为什么类型谓词的类型是什么?

2 个答案:

答案 0 :(得分:3)

用户定义的类型保护执行运行时检查以确定特定类型的值是否满足类型谓词。

如果值的类型与类型谓词中的类型之间没有关系,那么后卫就没有意义了。例如,TypeScript不允许像这样的用户定义的防护:

function isString(value: Date): value is string {
    return typeof value === "string";
}

并将影响此错误:

[ts] A type predicate's type must be assignable to its parameter's type.
Type 'string' is not assignable to type 'Date'.

Date值永远不会是string,所以保护是没有意义的:它的运行时检查是不必要的,应该始终返回false

当你指定一个通用的,用户定义的类型后卫时,T可以是任何东西,所以 - 就像Date一样 - 对于某些类型,类型后卫是没有意义的。

如果您真的不想使用any,可以使用空接口 - {} - 而不是:

function isString(value: {}): value is string {
    return typeof value === "string";
}

如果您还希望允许nullundefined值传递给警卫,您可以使用:

function isString(value: {} | null | undefined): value is string {
    return typeof value === "string";
}

关于错误消息,谓词类型必须可分配给值类型,因为类型保护用于检查具有较少特定类型的值是否实际上是具有更具体类型的值。例如,考虑一下这个警卫:

function isApe(value: Animal): value is Ape {
    return /* ... */
}

Ape可分配给Animal,但反之亦然。

答案 1 :(得分:1)

除了已接受的答案外,如果您碰巧需要使用针对混合的类型防护,您也会遇到此错误,因为is运算符的行为不像implements会。

interface Animal { ... }

interface Climber { ... }

interface Ape extends Animal, Climber { ... } 

const isClimberMixin = (animal: Animal): animal is Climber => ...

此类代码失败,因为Climber无法分配给Animal,因为它没有从其扩展。

如果无法避免使用混合模式,则解决方案是使用联合类型:

const isClimberMixin = (animal: Animal): animal is Animal & Climber => ...