Это сложно объяснить, но я пытаюсь отфильтровать определенный тип в подполе типа объекта.
Exclude<T, U>
, похоже, не работает с таким типом вложенности:
type PrivateName = { kind: "PrivateName" };
type Identifier = { kind: "Identifier" };
type NamedNode = {
name: PrivateName | Identifier;
};
type PrivateNamedNode = {
name: PrivateName;
};
declare const privateName: PrivateName;
type NamedNodeNonPrivate = Exclude<NamedNode, PrivateNamedNode>;
// I expected this next line to error, but it doesn't!
const x: NamedNodeNonPrivate = { name: privateName };
Почему не произошла ошибка последней строки? Должен ли я открыть отчет об ошибке?
Проверено на версиях TS: 3.5.1, 3.3.3, 3.1.6 и 3.0.1. ссылка на игровую площадку .
Потенциальная первопричина
Определение Exclude<T, U>
равно
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
В моем примере это кажется инертным, потому что NamedNode
не распространяется { name: PrivateName }
.
Изменение определения следующим образом приводит к желаемому сообщению об ошибке. К сожалению, это не рефакторинг, который я могу сделать вручную для всей кодовой базы:
type NamedNode = {
name: PrivateName
} | {
name: Identifier
}
Изоморфизм Карри-Говарда говорит нам, что типы произведений подобны соединению, а типы сумм подобны дизъюнкции.
В булевой логике верно следующее:
A & (B or C) implies (A & B) or (A & C)
Так что кажется разумным, что два определения NamedNode будут эквивалентны. Это отсутствие дистрибутивности:
- 1 причина моей проблемы выше
- 2 ошибка?