Type logical operators "and", "or" and "not" in extends clauses for mapped types
See original GitHub issueSearch Terms
Pretty much the ones in the title…
Suggestion
I would like the ability to use logical operators for types.
I am aware that not
is already planned as a feature, but I would like to make sure that this gets further extended to other logical operators, and I couldn’t find any hints that this is in your minds already.
Use Cases
Type composition, better readability…
Examples
Current tricks to get to the desired behavior:
type Not<T extends boolean> = T extends true ? false : true
type Or<A extends boolean, B extends boolean> = A extends true
? true
: B extends true
? true
: false
type Or3<A extends boolean, B extends boolean, C extends boolean> = Or<A, Or<B, C>>
type And<A extends boolean, B extends boolean> = A extends true
? B extends true
? true
: false
: false
A few arbitrary use cases:
type Primitive = boolean | number | string | symbol | null | undefined | void
type IsA<T, E> = T extends E ? true : false
type IsIndexSignature<P> = Or<IsA<string, P>, IsA<number, P>>
type IsCallback<F extends Function> = F extends (...args: any[]) => any
? And<Not<IsA<Parameters<F>[0], Primitive>>, IsA<Parameters<F>[0], Event>> extends true
? true
: false
: false
All together in the Playground: here
Desired syntactic sugar to write the same:
type IsIndexSignature<P> = IsA<string, P> or IsA<number, P>
type IsCallback<F extends Function> = F extends (...args: any[]) => any
? not IsA<Parameters<F>[0], Primitive> and IsA<Parameters<F>[0], Event> extends true
? true
: false
: false
It would make the most sense to accompany this with better support for boolean checks in type definitions. That is, to allow to use the ternary operator directly without the constant need for extends true
everywhere. Possibly, a new sort of “boolean type declaration” could be introduced, as to avoid having to propagate the boolean value all the way. For example, it should be possible to define KnownKeys (not full implementation here) like this:
type KnownKeys<T> = {
[P in keyof T]: IsIndexSignature<P> ? never : T[P]
}
Without the need to do:
type KnownKeys<T> = {
[P in keyof T]: IsIndexSignature<P> extends true ? never : T[P]
}
Checklist
My suggestion meets these guidelines:
- This wouldn’t be a breaking change in existing TypeScript/JavaScript code
- This wouldn’t change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn’t a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript’s Design Goals.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:8 (4 by maintainers)
I suppose “hack” is in the eye of the beholder, here. While the boolean literal types
true
andfalse
have evocative names, they just represent the types of boolean expressions that exist at runtime, they don’t behave like booleans at the type level.On the other hand,
unknown
andnever
are TypeScript’s names for the top and bottom types, respectively, which behave much more like booleans at the type level, which is what you’re asking for.The correspondence comes from equating a type to the statement that some value can be assigned to the type:
unknown
(top)unknown
? true.never
(bottom)never
? false.&
(intersection)A & B
iff it can toA
andB
.|
(union)A | B
iff it can to eitherA
orB
.not
* (complement)not A
iff it is not toA
.* well, if we get negated types
Hopefully that seems less arbitrary. Of course, there is no
not
yet, and propositional logic doesn’t usually have a primitive symbol for a ternary operator, so we’ll need our ownNot<T>
andCond<T,Y,N>
, as I said before:So, let’s look at your example:
Does that make more sense?
It’s sly that TypeScript Handbook describes Conditional Types as
(from https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types)
It never says Conditional Types will resolve to
true
orfalse
, so if we want to make specific the result of the condition, any binary values can work,unknown
andnever
are definitely ok.But not
true
/false
Everyone knows that at runtime the opposite of
true
isfalse
, and vice versa (except NaN, of course).But in negated types,
not true
means “anything not assignable totrue
” andnot false
means “anything not assignable tofalse
”.So for example
string
isnot true
andnot false
at the same time, what willstring ? true: false
resolve to?However I have to admit that using
unknown
andnever
is way more less intuitive.Originally I came here because I found a super cool blog post (in Chinese) that uses only the type system to build a script parser.
To chain multiple expressions, the author has to heavily use Conditional Types. So I wonder is there any proposal to add logical operators to the Conditional Types, as in JavaScript we can use
&&
.