Шаблон для общих параметров Typescript, которые иногда могут быть неопределенными? - PullRequest
1 голос
/ 10 мая 2019

Мне нужна иерархия классов, в которой некоторые реализации базового класса позволяют члену быть неопределенным, в то время как в других это никогда не будет иметь место. Я пытался понять, как вообще спросить об этом весь день, потому что я попробовал несколько подходов и столкнулся с различными языковыми ограничениями в зависимости от того, что я делаю. Я попытаюсь обобщить проблему на примере подхода, который я пробовал:

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 определено.

1 Ответ

1 голос
/ 10 мая 2019

Если вы открыты для использования параметра второго типа для представления возможности неопределенности, это работает:

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

Защитники типа не сузятся до параметра типа, но если есть два разных параметра типав союзе, который, кажется, работает просто отлично.

Дополнительный параметр типа (TUndefined) испарится, если мы передадим never, поскольку T | never равно T, и все работает, как и ожидалось, в производном классе Always.Если мы передадим undefined, то мы должны проверить неопределенность, как и ожидалось в Sometimes

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...