Subtraction types · Issue #4183 · microsoft/TypeScript (original) (raw)
Another type-safety measure. Sometimes it's desired to limit what developers can do with a value. Not allowing them to get to certain properties of it looks sufficient.
Example: We render HTML elements to PDF on the client side. In order to do so we need to run element.getBoundingClientRect
to get a bounding box in effect. It is an expensive operation that we wish could only be done once and then the result of it would be passed around along with the element to be rendered. Unfortunately nothing stops developers from ignoring that result and running the same method again and again as long as they can get to element.getBoundingClientRect
. Now I wish I could strip that method so no-one can see it once the box is calculated. Subtaction types would solve the problem.
type HTMLDivSpecifics = HTMLDivElement - Element;
Proposal
add a type operator that produces a new type out of 2 given types according to the rules that follow:
This feature would require a new negated type like number ~ string
which is a number
that cannot take number & string
.
As far as the precedence of new type operator, it should go:
- intersection
&
- union
|
- subtraction
-
so that number & boolean | string - string
, means ((number & boolean) | string) - string
Generics
- should produce a yet to be resolved type, should be stored as an expression which will produce either a type or an error when all type parameters are known
type Minus<A, B> = A - B; // yet to be calculated
Primitives
- if the left type (minued) isn't a sub-type of the right type (subtrahend) the
-
operation should result to an type error never
andany
should be specially handled
type C = number - number; // {} type C = number - {}; // number type C = {} - number; // error type C = number - string; // error type C = number - 0; // number ~ 0
type C = number | string - boolean; // error type C = number - void | null | undefined; // error
type C = number - any; // {} type C = any - any; // any type C = any - number; // any ~ number type C = any - never; // error; type C = never - any; // error; type C = number - never; // error type C = never - number; // error type C = never - never; // error
type C = number | string - string; // number type C = number - number | string; // {} type C = number | string - {}; // number | string
type C = number & string - boolean; // error type C = number & string - string; // number ~ string
Products
- only matching properties should be considered, non-matching properties of the left type should stay intact, non-matching properties of the right type should be disregarded
- if the names of 2 properties match their types are subject for
-
operation that produces the type of the resulting property of the same name - if applying
-
on 2 properties of the same name gives{}
, the property gets dropped from the resulting type
type C = {} - { x: number }; // {} type C = { x: number } - {}; // { x: number } type C = { x: {} } - { x: number }; // error type C = { x: number } - { x: {} }; // { x: number } type C = { x: number } - { y: number }; // { x: number } type C = { x: number } - { x: number }; // {} type C = { x: number | string } - { x: string }; // { x: number } type C = { x: number & string } - { x: string }; // { x: number ~ string } type C = { x: number } - { x: string }; // error
Functions (2 certain signatures)
- both functions must have the same number of parameters, otherwise it's an error
- types of corresponding parameters are subject to the
-
operator - types of results must 100% match and should be kept intact, otherwise it's an error
- if
-
on 2 parameters gives{}
the resulting parameter is{}
- if all resulting parameters are
{}
the resulting type is{}
type C = ((x: number) => string) - ((x: number) => string); // {} type C = ((x: number) => number) - ((x: number) => number); // (x: {}) => number type C = ((x: number | string) => string) - ((x: string) => string); // (x: number) => string type C = ((x: number) => string) - ((x: string) => string); // error type C = ((x: number | string) => string) - (() => string); // error type C = (() => string) - ((x: number) => string); // error
Overloads
- to be continued...