【问题标题】:Narrowing a generic return value in typescript缩小打字稿中的通用返回值
【发布时间】:2018-02-26 23:53:55
【问题描述】:

我有一个函数,它接受它的参数并返回一个可以处理这些参数的函数。不幸的是,我根本无法让 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&lt;Dog&gt;

我在这里做错了什么?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    问题在于 Typescript 不会根据方法中的代码分支来缩小泛型参数的范围。 instanceof 类型保护实际上只会将 animal 的类型从 T 更改为 T&amp;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

    【讨论】:

    • 我害怕那个。我也可以返回树皮作为 NoiseMaker。但就像你说的那样,我在一个我真的很想拥有它的地方失去了一些类型安全。感谢您的回复。
    • @user37078 是的,类型断言将解决问题,在这种情况下并不理想但必要
    猜你喜欢
    • 2015-10-07
    • 2020-08-06
    • 2020-01-10
    • 2023-02-07
    • 2019-04-25
    • 2017-01-15
    • 2018-07-30
    • 2019-09-26
    • 1970-01-01
    相关资源
    最近更新 更多