缩小打字稿中的通用返回值

时间:2018-02-26 15:47:46

标签: typescript

我有一个函数接受它的参数并返回一个可以处理这些参数的函数。不幸的是,我无法让TypeScript进行类型检查。以下是我的问题的简化示例:

type NoiseMaker<T extends Animal> = (animal: T) => void;

class Dog  {
    bark() {
        console.log('Woof! Woof!');
    }
}

class Cat  {
    meow() {
        console.log('Meow')
    }
}

type Animal = Dog | Cat;

const bark: NoiseMaker<Dog> = (dog: Dog) => {
    dog.bark();
}

function getNoiseMaker<T extends Animal>(animal: T): NoiseMaker<T> {
    if (animal instanceof Dog) {
        // T is a Dog then, right?
        return bark;  // ERROR: Type '(dog: Dog) => void' is not assignable to type 'NoiseMaker<T>'.
                      // Type 'T' is not assignable to type 'Dog'
    }
    else {
        throw new Error("I don't know that kind of animal");
    }
}

getNoiseMaker()返回一个适用于任何给定T的函数。一旦TypeScript确定T的类型是或扩展了Dog,为什么它不允许我返回bark,这是NoiseMaker<Dog>

我在这里做错了什么?

1 个答案:

答案 0 :(得分:1)

问题在于,Typescript不会根据方法中的代码分支来缩小泛型参数。 instanceof类型保护实际上只会将animal的类型从T更改为T&Dog,因此它会影响参数但不影响泛型类型。

最简单的解决方案是将通用签名保留为公共签名,但是您可以使用不同的实现签名来编写此代码(您将在函数内部松开某些类型的安全性,但公开它将完全相同)

function getNoiseMaker<T extends Animal>(animal: T): NoiseMaker<T> 
function getNoiseMaker(animal: Animal): NoiseMaker<Animal> {
    if (animal instanceof Dog) {
        return bark;  // Ok
    }
    else {
        throw new Error("I don't know that kind of animal");
    }
}

另一种选择是将bark投射到any