如何在打字稿中的自定义类型保护中使用instanceof?

lim*_*dev 6 narrowing typescript typeguards

我正在尝试使用创建自定义类型保护,instanceof但奇怪的是它在 else 子句中没有按预期工作

这是带有相关 Playground 链接的示例: Playground Link

class Person {}

class Animal {}

const isPerson = (obj: Person | Animal): obj is Person => obj instanceof Person;
const isAnimal = (obj: Person | Animal): obj is Animal => obj instanceof Animal;

const test: Person | Animal = new Person();

if(test instanceof Animal){
  test; // const test: Animal
}
else {
  test; // const test: Person
}

if(isAnimal(test)){
  test; // const test: Animal
}
else {
  test; // const test: never

}
Run Code Online (Sandbox Code Playgroud)

我希望 test 是Personelse 子句中的类型,但它是类型never......为什么?

我知道我也可以直接使用instanceof,但我更喜欢有一个更简洁的函数,就像创建的函数一样

更新:通过这个小编辑,类型保护工作正常......为什么?! 游乐场链接

class Person {
  private xxx = "xxx"
}

class Animal {
  private xxx = "xxx"
}

const isPerson = (obj: Person | Animal): obj is Person => obj instanceof Person;
const isAnimal = (obj: Person | Animal): obj is Animal => obj instanceof Animal;

declare const test: Person | Animal;

if(test instanceof Animal){
  test; // const test: Animal
}
else {
  test; // const test: Person
}

if(isAnimal(test)){
  test; // const test: Animal
}
else {
  test; // const test: Person
}
Run Code Online (Sandbox Code Playgroud)

ghy*_*ybs 6

TypeScript 执行结构类型。它看到的PersonAnimal实际上具有相同的结构。

因此,如果test is Animalfalse,则推断也是test is Person如此false。因此留下了结果never类型。

Person通过和中具有独特的属性Animal,现在它们的结构不同了,并且 TypeScript 可以正确地为您提供块Person中剩余的类型else

class Person {
  common = 0
  //person = 0
}

class Animal {
  common = 0
  animal = 0
}

const isPerson = (obj: Person | Animal): obj is Person => obj instanceof Person;
const isAnimal = (obj: Person | Animal): obj is Animal => obj instanceof Animal;

declare const test: Person | Animal;

if (test instanceof Animal) {
  test; // const test: Animal
}
else {
  test; // const test: Person
}

if (isAnimal(test)) {
  test; // const test: Animal
}
else {
  test; // const test: Person
}
Run Code Online (Sandbox Code Playgroud)

游乐场链接


注意:请注意const test: UnionType = oneOfTheTypes;,TypeScript 执行赋值缩小,因此它仍然能够推断出test实际上只是其中一种类型。


另一方面,缩小范围的instanceof工作方式有所不同:

在 JavaScript 中x instanceof Foo检查原型链是否x包含Foo.prototype.

TypeScript 显然复制了这种行为,因此它不应用结构类型,而是应用原型继承检查。


问题编辑后:

通过向 中添加一个唯一字段(或私有字段) ,我们明确地打破了以某种方式扩展 的Animal可能性。这与我上面的代码示例类似。PersonAnimal

所以现在 TypeScript 将它们视为两种完全不相关的类型,并且控制流的行为如您所期望的那样(即块中的testa )。Personelse


相反,应该Person 扩展 Animal,我们现在显式关联这两种类型,并且 TypeScript 现在知道 iftest不是 an Animal,它也不是一个Person(因此test回到块neverelse),包括使用instanceof!时。(因为在这种情况下我们创建了一个类继承)

class Animal {
  private xxx = "xxx"
}

class Person extends Animal {
  private xxx2 = "xxx"
}

const isPerson = (obj: Person | Animal): obj is Person => obj instanceof Person;
const isAnimal = (obj: Person | Animal): obj is Animal => obj instanceof Animal;

declare const test: Person | Animal;

if (test instanceof Animal) {
  test; // const test: Animal | Person
}
else {
  test; // const test: never
}

if (isAnimal(test)) {
  test; // const test: Animal | Person
}
else {
  test; // const test: never
}
Run Code Online (Sandbox Code Playgroud)

游乐场链接