有时可能无法定义的Typescript通用参数的模式?

时间:2019-05-10 13:45:41

标签: typescript generics inheritance

我想要一个类层次结构,其中基类的某些实现允许未定义成员,而在其他实现中则绝不会如此。我一直在尝试解决如何整天问问题,因为我尝试了几种方法,并根据自己的工作遇到了不同的语言限制。我将尝试用我尝试过的方法的一个例子来总结问题:

function f(foo: Foo): number { return foo.bar; }

class Foo { bar: number; }

abstract class Base<T extends Foo | undefined> {
    data: T;
    public f(): number {
        if (!this.data) { return -1; }
        return f(this.data); // bad: this.data is still `Foo | undefined` not just `Foo`
    }
}

class Always extends Base<Foo> {
  constructor() { super(); this.data = new Foo(); }
}

class Sometimes extends Base<Foo | undefined> {}

let a = new Always();
let s = new Sometimes();
a.data.bar = 1; // good: no error because `a.data` must not be undefined
s.data.bar = 1; // good: compiler flags this because s.data can be undefined

我上面标记为“坏”的那一行是问题所在–因为您仍然can't narrow a union generic无法使用简单的防护来断言data并非未定义。我当然可以写f(this.data as Foo),但我想避免在所有可能的地方都这样做。

我通过添加第二个泛型参数extends boolean来短暂地处理条件类型,但是我也无法使它起作用。基本上,我自己制定的所有解决方案都会破坏示例中的三行内容之一,其中f(this.data)a.data.bar始终有效,或者s.data.bar始终无效如果尚未检查是否定义了s.data

1 个答案:

答案 0 :(得分:1)

如果您愿意使用第二个类型参数来表示未定义的可能性,那么可以这样做:

function f(foo: Foo): number { return foo.bar; }

class Foo { bar: number; }


abstract class Base<T extends Foo, TUndefiend extends undefined> {
    data!: T | TUndefiend;
    public f(): number {
        if (!this.data) { return -1; }
        return f(this.data); // ok now
    }
}

class Always extends Base<Foo, never> {
  constructor() { super(); this.data = new Foo(); }
}

class Sometimes extends Base<Foo, undefined> {}

let a = new Always();
let s = new Sometimes();
a.data.bar = 1; // good: no error because `a.data` must not be undefined
s.data.bar = 1; // good: compiler flags this because s.data can be undefined

类型防护不会缩小为类型参数,但是如果在联合中存在两个不同的类型参数,则看起来工作得很好。

如果我们以TUndefinednever的方式进入T | never,则多余的类型参数(T)将消失,并且所有操作都将在派生的Always中按预期进行类。如果我们传入undefined,那么我们必须再次检查未定义,就像在Sometimes

中所期望的那样