Ошибка при вводе строкового литерала в поле при определении в интерфейсе - PullRequest
0 голосов
/ 19 сентября 2018

Когда тип строкового литерала определен в интерфейсе, я получаю неожиданное поведение.

interface IFoo {
    value: 'foo' | 'boo';
}

Когда я реализую интерфейс в классе, я получаю ошибку:

class Foo implements IFoo {
    value = 'foo';
}

Я получаю ошибку: Свойство 'Значение 'в типе' Foo 'нельзя назначить одному и тому же свойству в базовом типе' IFoo '.Но 'foo' является правильным значением для строкового литерала.

С другой стороны:

class Boo implements IFoo {
    value;
    constructor() {
        this.value = 'foo';
        this.value = 'boo';
        this.value = 'koo'; // must be an error Boo doesn't implement IFoo
    }
}
const test = new Boo();
test.value = 'koo';

Этот код не вызывает ошибок, но Boo.value имеет тип any,Я ожидал получить ошибку, что Boo не реализует IFoo, но ошибки нет.

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

class Koo implements IFoo {
    value: 'foo' | 'boo' = 'foo';
}

Итак, мне пришлось объявить enum:

enum Doos { foo = 'foo', boo = 'boo' }
interface IDoo {
    value: Doos;
}
class Doo implements IDoo {
    value = Doos.foo;
}
const test = new Doo();
test.value = Doos.boo;

Я понимаю, что это случилось, потому что компилятор ts получил тип Doo.value из назначенного значения в объявлении поля.Похоже, бесполезно объявлять поля строковых литералов в интерфейсах, или я делаю что-то не так.А также выяснил, что классы могут реализовывать интерфейсы с типом for для любых типов, так что дело за разработчиком.

1 Ответ

0 голосов
/ 19 сентября 2018

Проблема в том, что вы ожидаете, что implements IFoo повлияет на способ ввода поля класса.Это не.То, как это происходит, заключается в том, что поля класса вводятся так, как будто implements Foo не существует, и после того, как тип класса полностью разрешен, он проверяется на совместимость с реализованными интерфейсами.Посмотрев на это таким образом, ошибки имеют смысл.

class Foo implements IFoo {
    value = 'foo'; // this is typed as string, not as the string literal type and thus is not compatible with value in IFoo 
}
class Boo implements IFoo {
    // no type, no init, value is typed as any and any is compatible with 'foo' | 'boo' 
    // use -noImplicitAny to avoid such errors
    value; 
    constructor() {
        this.value = 'foo';
        this.value = 'boo';
        this.value = 'koo'; // 'koo' is compatible with any
    }
}

Когда вы используете перечисление, все работает, потому что если мы присваиваем значение перечисления полю, поле будет напечатано как перечисление.

Вы можете указать типполя value, либо в явном виде, либо в связи с интерфейсом IFoo:

class Foo implements IFoo {
    value: IFoo['value'] = 'foo';
}

Или, если поле равно readonly, оно будет напечатано как строковый литерал:

class Foo implements IFoo {
    readonly value = 'foo';
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...