Conditional types allow you to choose between two different output types by inspecting an input type. The syntax (inspired by TypeScript) is similar to a ternary expression:
type T = CheckType extends ExtendsType ? TrueType : FalseType;
If CheckType
is a subtype of ExtendsType
, the result is TrueType
, otherwise it is FalseType
.
We’ve also added new built-in utility types which make use of conditional types, like ReturnType<F>
, Parameters<F>
, Exclude<T, U>
, and Extract<T, U>
.
Basic Usage
The evaluating of the following conditional type is very similar to that of ternary expression:
CheckType extends ExtendsType ? TrueType : FalseType
You perform a type-level test of whether CheckType
is a subtype of ExtendsType
. If that’s the case, it will be evaluated to TrueType
. Otherwise, it will be evaluated to FalseType
.
As part of the same project, we also support infer types. Combined with generics, we can finally write down a utility type that reliably extracts the return type of a function:
type ReturnType<T: (...args: $ReadOnlyArray<empty>) => mixed> =
T extends (...args: $ReadOnlyArray<empty>) => infer Return
? Return
: any;
New Utility Types
Advanced utility types like conditional types are generally not useful for product code, but it is very expressive to let us create utility types in the user land. With conditional types, we can now provide the following types as Flow built-ins, without any special-cased support:
ReturnType<F>
: Obtain the return type of a function typeF
. e.g.ReturnType<(string, number)=>boolean> = boolean
Parameters<F>
: Obtain the parameters of a function typeF
in a tuple. e.g.Parameters<(string, number)=>boolean> = [string, number]
Exclude<T, U>
: Exclude fromT
those types that are assignable toU
. e.g.Exclude<1 | 2 | 3 | 4, 1 | 3> = 2 | 4
Extract<T, U>
: Extract fromT
those types that are assignable toU
. e.g.Extract<Car | Dog | Cat, Animal> = Dog | Cat
ThisParameterType<F>
: Extracts the type of thethis
parameter of a function typeF
. e.g.ThisParameterType<(this: foo, bar: string) => void> = foo
OmitThisParameter<F>
: Removes thethis
parameter from a function typeF
. e.g.OmitThisParameterType<(this: foo, bar: string) => void> = (bar: string) => void
In addition, the Exclude
type is necessary to power another long requested Omit type. Combined with mapped types, we hope to help you get rid of most of the confusing $Diff and $Rest, and $ObjMap type usages.
Fixed Array.flat typing
Through the distributive property of conditional type, we are finally able to fix the typing of Array.prototype.flat
. Now, the following code is correctly inferred to have type Array<number>
:
[1, [2, 3], 42, [65535]].flat()
Migration
The new utility types will be better replacements for most of the existing $Call
use cases. In most cases, we found that people are using $Call
only for extraction purposes. e.g.
type ExtractedPart = $Call<
<T>(($ReadOnlyArray<$ReadOnly<{foo: {bar: T}}>>) => string) => T,
SomeBigImportedType,
>;
can be rewritten as the following with much clearer intention with a combination of indexed access types and some of the new utility types:
type Extracted = Parameters<SomeBigImportedType>[0][number]['foo']['bar'];
Since conditional type is much clearer than $Call
, we have deprecated $Call
since v0.224.0. You can turn on deprecated-type
lint to find all existing uses of $Call
and replace them with appropriate types.
To support the new syntax, you need to update your version of Prettier, hermes-parser Babel plugin, hermes-eslint and more. Check out the adoption section in the docs for more details.