Мне нужна иерархия классов, в которой некоторые реализации базового класса позволяют члену быть неопределенным, в то время как в других это никогда не будет иметь место. Я пытался понять, как вообще спросить об этом весь день, потому что я попробовал несколько подходов и столкнулся с различными языковыми ограничениями в зависимости от того, что я делаю. Я попытаюсь обобщить проблему на примере подхода, который я пробовал:
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
Проблема в строке выше, которую я назвал "плохой", - поскольку вы все еще не можете сузить универсальный универсальный , нет способа использовать простую охрану, чтобы утверждать, что data
не является не определено. Конечно, я могу написать f(this.data as Foo)
, но я хочу избегать этого везде, где это возможно.
Я кратко поиграл с условными типами, добавив второй универсальный параметр, который extends boolean
, но я также не смог заставить это работать. По сути, все решения, которые я разработал, разбились на одну из трех строк с комментариями в примере: f(this.data)
, a.data.bar
всегда действителен или s.data.bar
всегда недействителен, если вы еще этого не сделали. проверил, что s.data
определено.