网站搜索

javascript - 在 TypeScript 中如何声明子类方法参数以更具体(无实现)?


例如,我可以在 TypeScript 中做以下事情吗?
我只是想在子类中缩小输入类型,以便更好地获得 IDE 的自动补全。我不想在里面像 super.foo(a) 那样编写实现。
(实际上,我正在继承 Node 的 EventEmitter,并且想缩小 on 和 emit 的 eventName 类型)

class Base {
    foo (a: string){
        // Do many things
    }
}

class Child {
    // I want to declare that this foo method can only accept these strings, and reuse the Base class's implementation
    declare foo (a: 'AAA'|'BBB'); 
}

我知道有一种仅类型的字段声明,通过在类字段前添加declare,可以仅为继承的字段重新声明一个更准确的类型,这非常方便。
所以我想知道是否有办法对继承的方法做同样的事情。

答案#1

您目前无法在方法上使用 declare 修饰符。对此,microsoft/TypeScript#38008 有一个未决功能请求,但目前它不是语言的一部分。这意味着您需要通过重新实现覆盖方法来解决这个问题:

class Child extends Base {
    foo(a: 'AAA' | 'BBB') {
        super.foo(a);
    }
}

或者通过将方法更改为函数值属性:

class Base {
    foo = (a: string) => {
        // Do many things
    }
}

class Child extends Base {
    declare foo: (a: 'AAA' | 'BBB') => void;
    //      ~~~ <-- error with --strictFunctionTypes
}

当然,你会看到,如果启用了 --strictFunctionTypes 编译器选项(属于 --strict 编译器选项套件的一部分),你会收到一个错误。子类假设 foo() 的参数比其父类期望的参数 更具体 是不安全的。这违反了参数类型的 逆变性(参见 TypeScript 中方差、协变、逆变和双变的区别),如果不小心,这可能会带来麻烦:

class Whoops extends Child {
    foo(k: 'AAA' | 'BBB') {
        ({ AAA: 1, BBB: 2 })[k].toFixed(1);
    }
}
function acceptBase(base: Base) { base.foo("oops") };
acceptBase(new Whoops()); // RUNTIME ERROR

假设你会小心。


目前支持这种做法的唯一方法是让 Base 在 foo() 的参数中成为泛型,然后为你的子类指定类型参数:

class Base<A extends string = string> {
    foo(a: A) {
        // Do many things
    }
}

class Child extends Base<"AAA" | "BBB">{ }

代码的 Playground 链接