mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Add fastpath to isRelatedTo for type references (#37481)
* Add fastpath to isRelatedTo for type references * Do not check intersections or unions to ignore propegating reference flags, properly set comparing jsx flag * Re-remove unneeded check * Just check for TypeFlags.Object * Remove else clause
This commit is contained in:
parent
8dd6b3a389
commit
edd4e0a42b
@ -15541,11 +15541,21 @@ namespace ts {
|
||||
* * Ternary.False if they are not related.
|
||||
*/
|
||||
function isRelatedTo(originalSource: Type, originalTarget: Type, reportErrors = false, headMessage?: DiagnosticMessage, intersectionState = IntersectionState.None): Ternary {
|
||||
// Before normalization: if `source` is type an object type, and `target` is primitive,
|
||||
// skip all the checks we don't need and just return `isSimpleTypeRelatedTo` result
|
||||
if (originalSource.flags & TypeFlags.Object && originalTarget.flags & TypeFlags.Primitive) {
|
||||
if (isSimpleTypeRelatedTo(originalSource, originalTarget, relation, reportErrors ? reportError : undefined)) {
|
||||
return Ternary.True;
|
||||
}
|
||||
reportErrorResults(originalSource, originalTarget, Ternary.False, !!(getObjectFlags(originalSource) & ObjectFlags.JsxAttributes));
|
||||
return Ternary.False;
|
||||
}
|
||||
|
||||
// Normalize the source and target types: Turn fresh literal types into regular literal types,
|
||||
// turn deferred type references into regular type references, simplify indexed access and
|
||||
// conditional types, and resolve substitution types to either the substitution (on the source
|
||||
// side) or the type variable (on the target side).
|
||||
let source = getNormalizedType(originalSource, /*writing*/ false);
|
||||
const source = getNormalizedType(originalSource, /*writing*/ false);
|
||||
let target = getNormalizedType(originalTarget, /*writing*/ true);
|
||||
|
||||
if (source === target) return Ternary.True;
|
||||
@ -15679,6 +15689,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For certain combinations involving intersections and optional, excess, or mismatched properties we need
|
||||
// an extra property check where the intersection is viewed as a single object. The following are motivating
|
||||
// examples that all should be errors, but aren't without this extra property check:
|
||||
@ -15702,47 +15713,51 @@ namespace ts {
|
||||
inPropertyCheck = false;
|
||||
}
|
||||
|
||||
if (!result && reportErrors) {
|
||||
source = originalSource.aliasSymbol ? originalSource : source;
|
||||
target = originalTarget.aliasSymbol ? originalTarget : target;
|
||||
let maybeSuppress = overrideNextErrorInfo > 0;
|
||||
if (maybeSuppress) {
|
||||
overrideNextErrorInfo--;
|
||||
}
|
||||
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
|
||||
const currentError = errorInfo;
|
||||
tryElaborateArrayLikeErrors(source, target, reportErrors);
|
||||
if (errorInfo !== currentError) {
|
||||
maybeSuppress = !!errorInfo;
|
||||
reportErrorResults(source, target, result, isComparingJsxAttributes);
|
||||
return result;
|
||||
|
||||
function reportErrorResults(source: Type, target: Type, result: Ternary, isComparingJsxAttributes: boolean) {
|
||||
if (!result && reportErrors) {
|
||||
source = originalSource.aliasSymbol ? originalSource : source;
|
||||
target = originalTarget.aliasSymbol ? originalTarget : target;
|
||||
let maybeSuppress = overrideNextErrorInfo > 0;
|
||||
if (maybeSuppress) {
|
||||
overrideNextErrorInfo--;
|
||||
}
|
||||
}
|
||||
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) {
|
||||
tryElaborateErrorsForPrimitivesAndObjects(source, target);
|
||||
}
|
||||
else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) {
|
||||
reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead);
|
||||
}
|
||||
else if (isComparingJsxAttributes && target.flags & TypeFlags.Intersection) {
|
||||
const targetTypes = (target as IntersectionType).types;
|
||||
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes, errorNode);
|
||||
const intrinsicClassAttributes = getJsxType(JsxNames.IntrinsicClassAttributes, errorNode);
|
||||
if (intrinsicAttributes !== errorType && intrinsicClassAttributes !== errorType &&
|
||||
(contains(targetTypes, intrinsicAttributes) || contains(targetTypes, intrinsicClassAttributes))) {
|
||||
// do not report top error
|
||||
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
|
||||
const currentError = errorInfo;
|
||||
tryElaborateArrayLikeErrors(source, target, reportErrors);
|
||||
if (errorInfo !== currentError) {
|
||||
maybeSuppress = !!errorInfo;
|
||||
}
|
||||
}
|
||||
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) {
|
||||
tryElaborateErrorsForPrimitivesAndObjects(source, target);
|
||||
}
|
||||
else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) {
|
||||
reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead);
|
||||
}
|
||||
else if (isComparingJsxAttributes && target.flags & TypeFlags.Intersection) {
|
||||
const targetTypes = (target as IntersectionType).types;
|
||||
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes, errorNode);
|
||||
const intrinsicClassAttributes = getJsxType(JsxNames.IntrinsicClassAttributes, errorNode);
|
||||
if (intrinsicAttributes !== errorType && intrinsicClassAttributes !== errorType &&
|
||||
(contains(targetTypes, intrinsicAttributes) || contains(targetTypes, intrinsicClassAttributes))) {
|
||||
// do not report top error
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
errorInfo = elaborateNeverIntersection(errorInfo, originalTarget);
|
||||
}
|
||||
if (!headMessage && maybeSuppress) {
|
||||
lastSkippedInfo = [source, target];
|
||||
// Used by, eg, missing property checking to replace the top-level message with a more informative one
|
||||
return result;
|
||||
}
|
||||
reportRelationError(headMessage, source, target);
|
||||
}
|
||||
else {
|
||||
errorInfo = elaborateNeverIntersection(errorInfo, originalTarget);
|
||||
}
|
||||
if (!headMessage && maybeSuppress) {
|
||||
lastSkippedInfo = [source, target];
|
||||
// Used by, eg, missing property checking to replace the top-level message with a more informative one
|
||||
return result;
|
||||
}
|
||||
reportRelationError(headMessage, source, target);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function isIdenticalTo(source: Type, target: Type): Ternary {
|
||||
|
||||
66
tests/baselines/reference/recursiveArrayNotCircular.js
Normal file
66
tests/baselines/reference/recursiveArrayNotCircular.js
Normal file
@ -0,0 +1,66 @@
|
||||
//// [recursiveArrayNotCircular.ts]
|
||||
type Action<T, P> = P extends void ? { type : T } : { type: T, payload: P }
|
||||
|
||||
enum ActionType {
|
||||
Foo,
|
||||
Bar,
|
||||
Baz,
|
||||
Batch
|
||||
}
|
||||
|
||||
type ReducerAction =
|
||||
| Action<ActionType.Bar, number>
|
||||
| Action<ActionType.Baz, boolean>
|
||||
| Action<ActionType.Foo, string>
|
||||
| Action<ActionType.Batch, ReducerAction[]>
|
||||
|
||||
function assertNever(a: never): never {
|
||||
throw new Error("Unreachable!");
|
||||
}
|
||||
|
||||
function reducer(action: ReducerAction): void {
|
||||
switch(action.type) {
|
||||
case ActionType.Bar:
|
||||
const x: number = action.payload;
|
||||
break;
|
||||
case ActionType.Baz:
|
||||
const y: boolean = action.payload;
|
||||
break;
|
||||
case ActionType.Foo:
|
||||
const z: string = action.payload;
|
||||
break;
|
||||
case ActionType.Batch:
|
||||
action.payload.map(reducer);
|
||||
break;
|
||||
default: return assertNever(action);
|
||||
}
|
||||
}
|
||||
|
||||
//// [recursiveArrayNotCircular.js]
|
||||
var ActionType;
|
||||
(function (ActionType) {
|
||||
ActionType[ActionType["Foo"] = 0] = "Foo";
|
||||
ActionType[ActionType["Bar"] = 1] = "Bar";
|
||||
ActionType[ActionType["Baz"] = 2] = "Baz";
|
||||
ActionType[ActionType["Batch"] = 3] = "Batch";
|
||||
})(ActionType || (ActionType = {}));
|
||||
function assertNever(a) {
|
||||
throw new Error("Unreachable!");
|
||||
}
|
||||
function reducer(action) {
|
||||
switch (action.type) {
|
||||
case ActionType.Bar:
|
||||
var x = action.payload;
|
||||
break;
|
||||
case ActionType.Baz:
|
||||
var y = action.payload;
|
||||
break;
|
||||
case ActionType.Foo:
|
||||
var z = action.payload;
|
||||
break;
|
||||
case ActionType.Batch:
|
||||
action.payload.map(reducer);
|
||||
break;
|
||||
default: return assertNever(action);
|
||||
}
|
||||
}
|
||||
126
tests/baselines/reference/recursiveArrayNotCircular.symbols
Normal file
126
tests/baselines/reference/recursiveArrayNotCircular.symbols
Normal file
@ -0,0 +1,126 @@
|
||||
=== tests/cases/compiler/recursiveArrayNotCircular.ts ===
|
||||
type Action<T, P> = P extends void ? { type : T } : { type: T, payload: P }
|
||||
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(recursiveArrayNotCircular.ts, 0, 12))
|
||||
>P : Symbol(P, Decl(recursiveArrayNotCircular.ts, 0, 14))
|
||||
>P : Symbol(P, Decl(recursiveArrayNotCircular.ts, 0, 14))
|
||||
>type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 38))
|
||||
>T : Symbol(T, Decl(recursiveArrayNotCircular.ts, 0, 12))
|
||||
>type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 53))
|
||||
>T : Symbol(T, Decl(recursiveArrayNotCircular.ts, 0, 12))
|
||||
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
|
||||
>P : Symbol(P, Decl(recursiveArrayNotCircular.ts, 0, 14))
|
||||
|
||||
enum ActionType {
|
||||
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
|
||||
|
||||
Foo,
|
||||
>Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17))
|
||||
|
||||
Bar,
|
||||
>Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8))
|
||||
|
||||
Baz,
|
||||
>Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8))
|
||||
|
||||
Batch
|
||||
>Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8))
|
||||
}
|
||||
|
||||
type ReducerAction =
|
||||
>ReducerAction : Symbol(ReducerAction, Decl(recursiveArrayNotCircular.ts, 7, 1))
|
||||
|
||||
| Action<ActionType.Bar, number>
|
||||
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
|
||||
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
|
||||
>Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8))
|
||||
|
||||
| Action<ActionType.Baz, boolean>
|
||||
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
|
||||
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
|
||||
>Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8))
|
||||
|
||||
| Action<ActionType.Foo, string>
|
||||
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
|
||||
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
|
||||
>Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17))
|
||||
|
||||
| Action<ActionType.Batch, ReducerAction[]>
|
||||
>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0))
|
||||
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
|
||||
>Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8))
|
||||
>ReducerAction : Symbol(ReducerAction, Decl(recursiveArrayNotCircular.ts, 7, 1))
|
||||
|
||||
function assertNever(a: never): never {
|
||||
>assertNever : Symbol(assertNever, Decl(recursiveArrayNotCircular.ts, 13, 45))
|
||||
>a : Symbol(a, Decl(recursiveArrayNotCircular.ts, 15, 21))
|
||||
|
||||
throw new Error("Unreachable!");
|
||||
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
|
||||
function reducer(action: ReducerAction): void {
|
||||
>reducer : Symbol(reducer, Decl(recursiveArrayNotCircular.ts, 17, 1))
|
||||
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
|
||||
>ReducerAction : Symbol(ReducerAction, Decl(recursiveArrayNotCircular.ts, 7, 1))
|
||||
|
||||
switch(action.type) {
|
||||
>action.type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53))
|
||||
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
|
||||
>type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53))
|
||||
|
||||
case ActionType.Bar:
|
||||
>ActionType.Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8))
|
||||
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
|
||||
>Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8))
|
||||
|
||||
const x: number = action.payload;
|
||||
>x : Symbol(x, Decl(recursiveArrayNotCircular.ts, 22, 17))
|
||||
>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
|
||||
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
|
||||
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
|
||||
|
||||
break;
|
||||
case ActionType.Baz:
|
||||
>ActionType.Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8))
|
||||
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
|
||||
>Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8))
|
||||
|
||||
const y: boolean = action.payload;
|
||||
>y : Symbol(y, Decl(recursiveArrayNotCircular.ts, 25, 17))
|
||||
>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62), Decl(recursiveArrayNotCircular.ts, 0, 62))
|
||||
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
|
||||
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62), Decl(recursiveArrayNotCircular.ts, 0, 62))
|
||||
|
||||
break;
|
||||
case ActionType.Foo:
|
||||
>ActionType.Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17))
|
||||
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
|
||||
>Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17))
|
||||
|
||||
const z: string = action.payload;
|
||||
>z : Symbol(z, Decl(recursiveArrayNotCircular.ts, 28, 17))
|
||||
>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
|
||||
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
|
||||
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
|
||||
|
||||
break;
|
||||
case ActionType.Batch:
|
||||
>ActionType.Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8))
|
||||
>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75))
|
||||
>Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8))
|
||||
|
||||
action.payload.map(reducer);
|
||||
>action.payload.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
|
||||
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
|
||||
>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62))
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>reducer : Symbol(reducer, Decl(recursiveArrayNotCircular.ts, 17, 1))
|
||||
|
||||
break;
|
||||
default: return assertNever(action);
|
||||
>assertNever : Symbol(assertNever, Decl(recursiveArrayNotCircular.ts, 13, 45))
|
||||
>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17))
|
||||
}
|
||||
}
|
||||
114
tests/baselines/reference/recursiveArrayNotCircular.types
Normal file
114
tests/baselines/reference/recursiveArrayNotCircular.types
Normal file
@ -0,0 +1,114 @@
|
||||
=== tests/cases/compiler/recursiveArrayNotCircular.ts ===
|
||||
type Action<T, P> = P extends void ? { type : T } : { type: T, payload: P }
|
||||
>Action : Action<T, P>
|
||||
>type : T
|
||||
>type : T
|
||||
>payload : P
|
||||
|
||||
enum ActionType {
|
||||
>ActionType : ActionType
|
||||
|
||||
Foo,
|
||||
>Foo : ActionType.Foo
|
||||
|
||||
Bar,
|
||||
>Bar : ActionType.Bar
|
||||
|
||||
Baz,
|
||||
>Baz : ActionType.Baz
|
||||
|
||||
Batch
|
||||
>Batch : ActionType.Batch
|
||||
}
|
||||
|
||||
type ReducerAction =
|
||||
>ReducerAction : ReducerAction
|
||||
|
||||
| Action<ActionType.Bar, number>
|
||||
>ActionType : any
|
||||
|
||||
| Action<ActionType.Baz, boolean>
|
||||
>ActionType : any
|
||||
|
||||
| Action<ActionType.Foo, string>
|
||||
>ActionType : any
|
||||
|
||||
| Action<ActionType.Batch, ReducerAction[]>
|
||||
>ActionType : any
|
||||
|
||||
function assertNever(a: never): never {
|
||||
>assertNever : (a: never) => never
|
||||
>a : never
|
||||
|
||||
throw new Error("Unreachable!");
|
||||
>new Error("Unreachable!") : Error
|
||||
>Error : ErrorConstructor
|
||||
>"Unreachable!" : "Unreachable!"
|
||||
}
|
||||
|
||||
function reducer(action: ReducerAction): void {
|
||||
>reducer : (action: ReducerAction) => void
|
||||
>action : ReducerAction
|
||||
|
||||
switch(action.type) {
|
||||
>action.type : ActionType
|
||||
>action : ReducerAction
|
||||
>type : ActionType
|
||||
|
||||
case ActionType.Bar:
|
||||
>ActionType.Bar : ActionType.Bar
|
||||
>ActionType : typeof ActionType
|
||||
>Bar : ActionType.Bar
|
||||
|
||||
const x: number = action.payload;
|
||||
>x : number
|
||||
>action.payload : number
|
||||
>action : { type: ActionType.Bar; payload: number; }
|
||||
>payload : number
|
||||
|
||||
break;
|
||||
case ActionType.Baz:
|
||||
>ActionType.Baz : ActionType.Baz
|
||||
>ActionType : typeof ActionType
|
||||
>Baz : ActionType.Baz
|
||||
|
||||
const y: boolean = action.payload;
|
||||
>y : boolean
|
||||
>action.payload : boolean
|
||||
>action : { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; }
|
||||
>payload : boolean
|
||||
|
||||
break;
|
||||
case ActionType.Foo:
|
||||
>ActionType.Foo : ActionType.Foo
|
||||
>ActionType : typeof ActionType
|
||||
>Foo : ActionType.Foo
|
||||
|
||||
const z: string = action.payload;
|
||||
>z : string
|
||||
>action.payload : string
|
||||
>action : { type: ActionType.Foo; payload: string; }
|
||||
>payload : string
|
||||
|
||||
break;
|
||||
case ActionType.Batch:
|
||||
>ActionType.Batch : ActionType.Batch
|
||||
>ActionType : typeof ActionType
|
||||
>Batch : ActionType.Batch
|
||||
|
||||
action.payload.map(reducer);
|
||||
>action.payload.map(reducer) : void[]
|
||||
>action.payload.map : <U>(callbackfn: (value: ReducerAction, index: number, array: ReducerAction[]) => U, thisArg?: any) => U[]
|
||||
>action.payload : ReducerAction[]
|
||||
>action : { type: ActionType.Batch; payload: ReducerAction[]; }
|
||||
>payload : ReducerAction[]
|
||||
>map : <U>(callbackfn: (value: ReducerAction, index: number, array: ReducerAction[]) => U, thisArg?: any) => U[]
|
||||
>reducer : (action: ReducerAction) => void
|
||||
|
||||
break;
|
||||
default: return assertNever(action);
|
||||
>assertNever(action) : never
|
||||
>assertNever : (a: never) => never
|
||||
>action : never
|
||||
}
|
||||
}
|
||||
36
tests/cases/compiler/recursiveArrayNotCircular.ts
Normal file
36
tests/cases/compiler/recursiveArrayNotCircular.ts
Normal file
@ -0,0 +1,36 @@
|
||||
type Action<T, P> = P extends void ? { type : T } : { type: T, payload: P }
|
||||
|
||||
enum ActionType {
|
||||
Foo,
|
||||
Bar,
|
||||
Baz,
|
||||
Batch
|
||||
}
|
||||
|
||||
type ReducerAction =
|
||||
| Action<ActionType.Bar, number>
|
||||
| Action<ActionType.Baz, boolean>
|
||||
| Action<ActionType.Foo, string>
|
||||
| Action<ActionType.Batch, ReducerAction[]>
|
||||
|
||||
function assertNever(a: never): never {
|
||||
throw new Error("Unreachable!");
|
||||
}
|
||||
|
||||
function reducer(action: ReducerAction): void {
|
||||
switch(action.type) {
|
||||
case ActionType.Bar:
|
||||
const x: number = action.payload;
|
||||
break;
|
||||
case ActionType.Baz:
|
||||
const y: boolean = action.payload;
|
||||
break;
|
||||
case ActionType.Foo:
|
||||
const z: string = action.payload;
|
||||
break;
|
||||
case ActionType.Batch:
|
||||
action.payload.map(reducer);
|
||||
break;
|
||||
default: return assertNever(action);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user