Почему Partial принимает дополнительные свойства другого типа? - PullRequest
2 голосов
/ 12 июня 2019

С учетом интерфейсов A и B, которые содержат общее свойство x1

interface A {
  a1: number;
  a2: number;
  x1: number;  // <<<<
}

interface B{
  b1: number;
  x1: number;  // <<<<
}

И с учетом реализаций a и b

let a: A = {a1: 1, a2: 1, x1: 1};
let b: B = {b1: 1, x1: 1};

Это просто проходитдаже если b1 не принадлежит Partial<A>:

let partialA: Partial<A> = b;

, но это не удается, поскольку b1 не относится к Partial<A>:

let partialA: Partial<A> = {b1: 1, x1: 1};

Может кто-нибудь, пожалуйстаскажи мне почему?

1 Ответ

2 голосов
/ 12 июня 2019

Это будет небольшая поездка, так что держитесь здесь со мной:

Обычно подтип должен быть назначен базовому типу.В Typescript тип с большим количеством свойств должен быть назначен для типа, где ожидается тип с подмножеством свойств.Так, например, это допустимо:

let source = { a1: 0, a2: 0}
let target: { a1: number } = source

Теперь, что удивительно, из-за того, как работает структурная типизация, Partial<A> является подтипом Partial<B>, а Partial<B> является подтипом Partial<A>.Дополнительные свойства могут отсутствовать в подтипе, поэтому дополнительные свойства, отсутствующие в типе, не исключают тип из подтипа.Если мы удалим необязательные свойства, у нас останется {}, который может быть базовым типом любого другого типа.Компилятор соглашается со мной в этом вопросе, если мы попросим его ответить на этот вопрос о подтипах, используя условные типы:

type q1 = Partial<A> extends Partial<B> ? "Y" : "N" // Y
type q2  = Partial<B> extends Partial<A> ? "Y" : "N" // Y

Существует одно исключение из этого (хорошо, может быть, два), присвоение литералов объекта непосредственно ссылкеопределенного типа.Это называется проверкой избыточных свойств, так как если бы мы выполнили вышеприведенное присваивание напрямую, мы получили бы ошибку:

let target: { a1: number } = { a1: 0, a2: 0} // err  

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

let partialA: Partial<A> = {b1: 1, x1: 1};

Но при проверке избыточных свойств срабатывает только прямое присвоение литерала объекта переменной определенного типа.Таким образом, при присваивании let partialA: Partial<A> = b; оно не вызовет ошибки при проверке избыточных свойств.

Еще одно осложнение заключается в том, что Partial<A> - это то, что называется слабым типом.Из PR введены проверки для такого типа:

Слабым типом является тот, который имеет только дополнительные свойства и не является пустым.Поскольку эти типы можно назначать из чего угодно, кроме типов с совпадающими не присваиваемыми свойствами, они очень слабо проверяются типами.Простым решением здесь является требование, чтобы тип назначался слабому типу только в том случае, если он не является полностью непересекающимся.

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

let k = { foo: 0 }
let partialA: Partial<A> = k;

Тип k является подтипом Partial<A>, уверен, что k имеетничего общего с Partial<A>, но это не проблема.В конце концов, Partial<A> не требует каких-либо свойств, поэтому k не нуждается ни в каком, и у него есть одно дополнительное свойство, которое, как правило, делает подтип, становится более конкретным путем добавления членов.

Причина, по которой приведенный выше кодовый код является ошибкой, заключается в том, что PR являются ссылками выше, в которых постулируется, что назначение совершенно несвязанного типа слабому типу, вероятно, является ошибкой.Поэтому, как и при проверке избыточных свойств, было введено правило (правило, которое снова нарушает идею, что подтип назначается базовому типу), которое запрещает такие назначения.

В вашем случае, однако, Partial<A> и Partial<B> имеютчто-то общее x1, поэтому это правило не улавливает возможно ошибочное назначение.Хотя это и удивительно, поскольку и Partial<A>, и Partial<B> не имеют обязательных свойств, они являются подтипами друг друга, и если нет особой причины (такой как проверка избыточных свойств или это правило обнаружения очень слабых типов), присваивание разрешено.

...