Этот пример Typescript нарушает принцип подстановки Лискова? - PullRequest
1 голос
/ 05 октября 2019

У меня есть следующий код:

type T = { foo: string }
var t: T = { foo: 'foo' }

interface S { foo: string }
var s: S = t

Итак, мы знаем, что T < S.

Как насчет этого?

t = s

Хорошо, так что S < Tтакже верно.

Мы можем подразумевать, что S == T.

Теперь введем U:

type U = { [key: string]: string }
var u: U = t

Итак T < U. Пока все хорошо.

Но подождите!

u = s // Error!

Это нарушает принцип подстановки Лискова (LSP):

, если S является подтипомT, тогда объекты типа T могут быть заменены объектами типа S

Является ли это нарушением LSP? Имеет ли значение, если это так или нет?

Принципы в стороне, это выглядит довольно глупо:

u = s    // Error!
u = <T>s // Ok!

Это будет считаться ошибкой? Конечно, компилятор мог бы сделать это сам по себе, нет?

1 Ответ

1 голос
/ 05 октября 2019

Система типов TypeScript неверна в некоторых местах;Вы нашли эту проблему , в которой псевдонимы типов , но не интерфейсы , получают подписи неявного индекса . Предоставление типа неявной подписи индекса полезно, но в целом небезопасно. Обратите внимание:

const fooBar = { foo: "foo", bar: 123 };
const tFooBar: T = fooBar; // okay
const uFooBar: U = tFooBar; // okay?
const whoopsie = uFooBar.bar; // string at compile time, number at runtime?!
console.log(whoopsie);

Значение fooBar является допустимым T, поскольку оно имеет свойство foo типа string. Таким образом, вы можете назначить его на tFooBar. И затем, так как TypeScript позволяет вам присвоить значение типа T переменной типа U, вы можете присвоить tFooBar uFooBar. И теперь необоснованность разоблачена, если вы прочитаете свойство bar uFooBar. Это должно быть string в соответствии с U, но это number. Упс.

Неявные подписи индекса полезны, потому что часто функции требуют значений с подписью индекса, и это полезно для значений, чьи известные свойства соответствуют подписи индекса, чтобы быть принятыми. Итак, у нас есть эта полезная вещь, которая может привести к небезопасному поведению. Что должно быть сделано?

По-видимому, текущее правило для TypeScript:

  • объектным литералам / анонимным типам присваиваются неявные индексные сигнатуры
  • псевдонимам типов присваивается неявный индексподписи
  • интерфейсам НЕ даны неявные индексные подписи

Очевидно, что последнее является преднамеренным и не является ошибкой, согласно этому комментарию @ RyanCavanaugh :

Просто чтобы заполнить людей, это поведение в настоящее время разработано. Поскольку интерфейсы могут быть дополнены дополнительными объявлениями, а псевдонимы типов не могут, «безопаснее» (тяжелые кавычки на этом) выводить неявную сигнатуру индекса для псевдонимов типов, чем для интерфейсов. Но мы рассмотрим это и для интерфейсов, если это кажется целесообразным.

Таким образом, мысль о том, что объединение объявлений может нарушить совместимость интерфейса с индексом-подписью, но псевдонимы типов не могут. Возможно, они готовы его изменить, и если у вас есть убедительный вариант использования, вы можете обратиться к проблеме Github и упомянуть об этом.

Хорошо, надеюсь, это поможет;удачи!

Ссылка на код

...