From 4e577f969e456043e00e6d007dc5d0cc7e421220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 8 Sep 2024 22:55:17 +0200 Subject: [PATCH] No contextual typing from return types for boolean literals deeper in instantiables --- src/compiler/checker.ts | 24 +- .../reference/arrayLiteralInference.types | 8 +- ...ontextuallyTypedBooleanLiterals.errors.txt | 78 ++++++ .../contextuallyTypedBooleanLiterals.js | 106 +++++++- .../contextuallyTypedBooleanLiterals.symbols | 163 +++++++++-- .../contextuallyTypedBooleanLiterals.types | 254 +++++++++++++++++- .../contextuallyTypedBooleanLiterals.ts | 45 +++- 7 files changed, 613 insertions(+), 65 deletions(-) create mode 100644 tests/baselines/reference/contextuallyTypedBooleanLiterals.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4d4f1bf69a808..ffb39f3644229 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32065,8 +32065,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // If the given contextual type contains instantiable types and if a mapper representing // return type inferences is available, instantiate those types using that mapper. function instantiateContextualType(contextualType: Type | undefined, node: Node, contextFlags: ContextFlags | undefined): Type | undefined { - if (contextualType && maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) { - const inferenceContext = getInferenceContext(node); + if (!contextualType) { + return; + } + let inferenceContext: InferenceContext | undefined; + if (maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) { + inferenceContext = getInferenceContext(node); // If no inferences have been made, and none of the type parameters for which we are inferring // specify default types, nothing is gained from instantiating as type parameters would just be // replaced with their constraints similar to the apparent type. @@ -32077,16 +32081,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (inferenceContext?.returnMapper) { // For other purposes (e.g. determining whether to produce literal types) we only - // incorporate inferences made from the return type in a function call. We remove - // the 'boolean' type from the contextual type such that contextually typed boolean - // literals actually end up widening to 'boolean' (see #48363). - const type = instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper); - return type.flags & TypeFlags.Union && containsType((type as UnionType).types, regularFalseType) && containsType((type as UnionType).types, regularTrueType) ? - filterType(type, t => t !== regularFalseType && t !== regularTrueType) : - type; + // incorporate inferences made from the return type in a function call. + contextualType = instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper); } } - return contextualType; + + // We remove the 'boolean' type from the contextual type from return types such that contextually typed boolean + // literals actually end up widening to 'boolean' (see #48363). + return contextualType.flags & TypeFlags.Union && (inferenceContext ??= getInferenceContext(node))?.returnMapper && containsType((contextualType as UnionType).types, regularFalseType) && containsType((contextualType as UnionType).types, regularTrueType) ? + filterType(contextualType, t => t !== regularFalseType && t !== regularTrueType) : + contextualType; } // This function is similar to instantiateType, except that (a) it only instantiates types that diff --git a/tests/baselines/reference/arrayLiteralInference.types b/tests/baselines/reference/arrayLiteralInference.types index 6520794bbf907..68e8eef78a6ab 100644 --- a/tests/baselines/reference/arrayLiteralInference.types +++ b/tests/baselines/reference/arrayLiteralInference.types @@ -190,8 +190,8 @@ let b1: { x: boolean }[] = foo({ x: true }, { x: false }); > : ^^^^^ ^^^^^ >x : boolean > : ^^^^^^^ ->foo({ x: true }, { x: false }) : ({ x: true; } | { x: false; })[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo({ x: true }, { x: false }) : { x: boolean; }[] +> : ^^^^^^^^^^^^^^^^^ >foo : (...args: T[]) => T[] > : ^ ^^^^^ ^^ ^^^^^ >{ x: true } : { x: true; } @@ -210,8 +210,8 @@ let b1: { x: boolean }[] = foo({ x: true }, { x: false }); let b2: boolean[][] = foo([true], [false]); >b2 : boolean[][] > : ^^^^^^^^^^^ ->foo([true], [false]) : (true[] | false[])[] -> : ^^^^^^^^^^^^^^^^^^^^ +>foo([true], [false]) : boolean[][] +> : ^^^^^^^^^^^ >foo : (...args: T[]) => T[] > : ^ ^^^^^ ^^ ^^^^^ >[true] : true[] diff --git a/tests/baselines/reference/contextuallyTypedBooleanLiterals.errors.txt b/tests/baselines/reference/contextuallyTypedBooleanLiterals.errors.txt new file mode 100644 index 0000000000000..f04a8f4cd1bc0 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedBooleanLiterals.errors.txt @@ -0,0 +1,78 @@ +contextuallyTypedBooleanLiterals.ts(32,7): error TS2322: Type 'Box<{ prop: false; other: number; }>' is not assignable to type 'Box<{ prop: true; other: string; } | { prop: false; other: number; }>'. + Types of property 'set' are incompatible. + Type '(value: { prop: false; other: number; }) => void' is not assignable to type '(value: { prop: true; other: string; } | { prop: false; other: number; }) => void'. + Types of parameters 'value' and 'value' are incompatible. + Type '{ prop: true; other: string; } | { prop: false; other: number; }' is not assignable to type '{ prop: false; other: number; }'. + Type '{ prop: true; other: string; }' is not assignable to type '{ prop: false; other: number; }'. + Types of property 'prop' are incompatible. + Type 'true' is not assignable to type 'false'. + + +==== contextuallyTypedBooleanLiterals.ts (1 errors) ==== + // Repro from #48363 + + type Box = { + get: () => T; + set: (value: T) => void; + }; + + declare function box(value: T): Box; + + const bn1 = box(0); // Box + const bn2: Box = box(0); // Ok, Box + + const bb1 = box(false); // Box + const bb2: Box = box(false); // Ok, Box + + // https://github.com/microsoft/TypeScript/issues/59754 + + const bn3 = box({ prop: 0 }); // Box<{ prop: number }> + const bn4: Box<{ prop: number }> = box({ prop: 0 }); // Ok, Box<{ prop: number }> + + const bb3 = box({ prop: false }); // Box + const bb4: Box<{ prop: boolean }> = box({ prop: false }); // Ok, Box<{ prop: boolean }> + + const bn5 = box([0]); // Box<[number]> + const bn6: Box<[number]> = box([0]); // Ok, Box<[number]> + + const bb5 = box([false]); // Box<[boolean]> + const bb6: Box<[boolean]> = box([false]); // Ok, Box<[boolean]> + + const bb7: Box<{ deep: { prop: boolean } }> = box({ deep: { prop: false } }); // Ok, Box<{ deep: { prop: boolean } }> + + const bb8: Box<{ prop: true; other: string } | { prop: false; other: number }> = box({ prop: false, other: 0 }); // Error (T is invariant), Box<{ prop: false; other: number }> + ~~~ +!!! error TS2322: Type 'Box<{ prop: false; other: number; }>' is not assignable to type 'Box<{ prop: true; other: string; } | { prop: false; other: number; }>'. +!!! error TS2322: Types of property 'set' are incompatible. +!!! error TS2322: Type '(value: { prop: false; other: number; }) => void' is not assignable to type '(value: { prop: true; other: string; } | { prop: false; other: number; }) => void'. +!!! error TS2322: Types of parameters 'value' and 'value' are incompatible. +!!! error TS2322: Type '{ prop: true; other: string; } | { prop: false; other: number; }' is not assignable to type '{ prop: false; other: number; }'. +!!! error TS2322: Type '{ prop: true; other: string; }' is not assignable to type '{ prop: false; other: number; }'. +!!! error TS2322: Types of property 'prop' are incompatible. +!!! error TS2322: Type 'true' is not assignable to type 'false'. + + const bb9: Box = box(false); // Ok, Box + + const bb10: Box<{ prop: false }> = box({ prop: false }); // Ok, Box<{ prop: false }> + + type Box2 = { + get: () => T; + }; + + const bb11: Box2<{ prop: true; other: string } | { prop: false; other: number }> = box2({ prop: false, other: 0 }); // Ok, Box2<{ prop: false; other: number }> + + declare function box2(value: T): Box2; + + // Repro from #48150 + + interface Observable + { + (): T; + (value: T): any; + } + + declare function observable(value: T): Observable; + + const x: Observable = observable(false); + + \ No newline at end of file diff --git a/tests/baselines/reference/contextuallyTypedBooleanLiterals.js b/tests/baselines/reference/contextuallyTypedBooleanLiterals.js index 93418aebf474a..782d0af8ebf24 100644 --- a/tests/baselines/reference/contextuallyTypedBooleanLiterals.js +++ b/tests/baselines/reference/contextuallyTypedBooleanLiterals.js @@ -4,17 +4,47 @@ // Repro from #48363 type Box = { - get: () => T, - set: (value: T) => void -} + get: () => T; + set: (value: T) => void; +}; declare function box(value: T): Box; -const bn1 = box(0); // Box -const bn2: Box = box(0); // Ok +const bn1 = box(0); // Box +const bn2: Box = box(0); // Ok, Box + +const bb1 = box(false); // Box +const bb2: Box = box(false); // Ok, Box + +// https://github.com/microsoft/TypeScript/issues/59754 + +const bn3 = box({ prop: 0 }); // Box<{ prop: number }> +const bn4: Box<{ prop: number }> = box({ prop: 0 }); // Ok, Box<{ prop: number }> + +const bb3 = box({ prop: false }); // Box +const bb4: Box<{ prop: boolean }> = box({ prop: false }); // Ok, Box<{ prop: boolean }> + +const bn5 = box([0]); // Box<[number]> +const bn6: Box<[number]> = box([0]); // Ok, Box<[number]> + +const bb5 = box([false]); // Box<[boolean]> +const bb6: Box<[boolean]> = box([false]); // Ok, Box<[boolean]> + +const bb7: Box<{ deep: { prop: boolean } }> = box({ deep: { prop: false } }); // Ok, Box<{ deep: { prop: boolean } }> -const bb1 = box(false); // Box -const bb2: Box = box(false); // Error, box not assignable to Box +const bb8: Box<{ prop: true; other: string } | { prop: false; other: number }> = box({ prop: false, other: 0 }); // Error (T is invariant), Box<{ prop: false; other: number }> + +const bb9: Box = box(false); // Ok, Box + +const bb10: Box<{ prop: false }> = box({ prop: false }); // Ok, Box<{ prop: false }> + +type Box2 = { + get: () => T; +}; + +const bb11: Box2<{ prop: true; other: string } | { prop: false; other: number }> = box2({ prop: false, other: 0 }); // Ok, Box2<{ prop: false; other: number }> + +declare function box2(value: T): Box2; // Repro from #48150 @@ -27,15 +57,30 @@ interface Observable declare function observable(value: T): Observable; const x: Observable = observable(false); + //// [contextuallyTypedBooleanLiterals.js] "use strict"; // Repro from #48363 var bn1 = box(0); // Box -var bn2 = box(0); // Ok +var bn2 = box(0); // Ok, Box var bb1 = box(false); // Box -var bb2 = box(false); // Error, box not assignable to Box +var bb2 = box(false); // Ok, Box +// https://github.com/microsoft/TypeScript/issues/59754 +var bn3 = box({ prop: 0 }); // Box<{ prop: number }> +var bn4 = box({ prop: 0 }); // Ok, Box<{ prop: number }> +var bb3 = box({ prop: false }); // Box +var bb4 = box({ prop: false }); // Ok, Box<{ prop: boolean }> +var bn5 = box([0]); // Box<[number]> +var bn6 = box([0]); // Ok, Box<[number]> +var bb5 = box([false]); // Box<[boolean]> +var bb6 = box([false]); // Ok, Box<[boolean]> +var bb7 = box({ deep: { prop: false } }); // Ok, Box<{ deep: { prop: boolean } }> +var bb8 = box({ prop: false, other: 0 }); // Error (T is invariant), Box<{ prop: false; other: number }> +var bb9 = box(false); // Ok, Box +var bb10 = box({ prop: false }); // Ok, Box<{ prop: false }> +var bb11 = box2({ prop: false, other: 0 }); // Ok, Box2<{ prop: false; other: number }> var x = observable(false); @@ -49,6 +94,49 @@ declare const bn1: Box; declare const bn2: Box; declare const bb1: Box; declare const bb2: Box; +declare const bn3: Box<{ + prop: number; +}>; +declare const bn4: Box<{ + prop: number; +}>; +declare const bb3: Box<{ + prop: boolean; +}>; +declare const bb4: Box<{ + prop: boolean; +}>; +declare const bn5: Box; +declare const bn6: Box<[number]>; +declare const bb5: Box; +declare const bb6: Box<[boolean]>; +declare const bb7: Box<{ + deep: { + prop: boolean; + }; +}>; +declare const bb8: Box<{ + prop: true; + other: string; +} | { + prop: false; + other: number; +}>; +declare const bb9: Box; +declare const bb10: Box<{ + prop: false; +}>; +type Box2 = { + get: () => T; +}; +declare const bb11: Box2<{ + prop: true; + other: string; +} | { + prop: false; + other: number; +}>; +declare function box2(value: T): Box2; interface Observable { (): T; (value: T): any; diff --git a/tests/baselines/reference/contextuallyTypedBooleanLiterals.symbols b/tests/baselines/reference/contextuallyTypedBooleanLiterals.symbols index 79821d4ccbec2..3a652f170102d 100644 --- a/tests/baselines/reference/contextuallyTypedBooleanLiterals.symbols +++ b/tests/baselines/reference/contextuallyTypedBooleanLiterals.symbols @@ -7,66 +7,173 @@ type Box = { >Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0)) >T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 2, 9)) - get: () => T, + get: () => T; >get : Symbol(get, Decl(contextuallyTypedBooleanLiterals.ts, 2, 15)) >T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 2, 9)) - set: (value: T) => void ->set : Symbol(set, Decl(contextuallyTypedBooleanLiterals.ts, 3, 17)) ->value : Symbol(value, Decl(contextuallyTypedBooleanLiterals.ts, 4, 10)) + set: (value: T) => void; +>set : Symbol(set, Decl(contextuallyTypedBooleanLiterals.ts, 3, 15)) +>value : Symbol(value, Decl(contextuallyTypedBooleanLiterals.ts, 4, 8)) >T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 2, 9)) -} + +}; declare function box(value: T): Box; ->box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 1)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) >T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 7, 21)) >value : Symbol(value, Decl(contextuallyTypedBooleanLiterals.ts, 7, 24)) >T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 7, 21)) >Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0)) >T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 7, 21)) -const bn1 = box(0); // Box +const bn1 = box(0); // Box >bn1 : Symbol(bn1, Decl(contextuallyTypedBooleanLiterals.ts, 9, 5)) ->box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 1)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) -const bn2: Box = box(0); // Ok +const bn2: Box = box(0); // Ok, Box >bn2 : Symbol(bn2, Decl(contextuallyTypedBooleanLiterals.ts, 10, 5)) >Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0)) ->box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 1)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) -const bb1 = box(false); // Box +const bb1 = box(false); // Box >bb1 : Symbol(bb1, Decl(contextuallyTypedBooleanLiterals.ts, 12, 5)) ->box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 1)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) -const bb2: Box = box(false); // Error, box not assignable to Box +const bb2: Box = box(false); // Ok, Box >bb2 : Symbol(bb2, Decl(contextuallyTypedBooleanLiterals.ts, 13, 5)) >Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0)) ->box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 1)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) + +// https://github.com/microsoft/TypeScript/issues/59754 + +const bn3 = box({ prop: 0 }); // Box<{ prop: number }> +>bn3 : Symbol(bn3, Decl(contextuallyTypedBooleanLiterals.ts, 17, 5)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 17, 17)) + +const bn4: Box<{ prop: number }> = box({ prop: 0 }); // Ok, Box<{ prop: number }> +>bn4 : Symbol(bn4, Decl(contextuallyTypedBooleanLiterals.ts, 18, 5)) +>Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 18, 16)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 18, 40)) + +const bb3 = box({ prop: false }); // Box +>bb3 : Symbol(bb3, Decl(contextuallyTypedBooleanLiterals.ts, 20, 5)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 20, 17)) + +const bb4: Box<{ prop: boolean }> = box({ prop: false }); // Ok, Box<{ prop: boolean }> +>bb4 : Symbol(bb4, Decl(contextuallyTypedBooleanLiterals.ts, 21, 5)) +>Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 21, 16)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 21, 41)) + +const bn5 = box([0]); // Box<[number]> +>bn5 : Symbol(bn5, Decl(contextuallyTypedBooleanLiterals.ts, 23, 5)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) + +const bn6: Box<[number]> = box([0]); // Ok, Box<[number]> +>bn6 : Symbol(bn6, Decl(contextuallyTypedBooleanLiterals.ts, 24, 5)) +>Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) + +const bb5 = box([false]); // Box<[boolean]> +>bb5 : Symbol(bb5, Decl(contextuallyTypedBooleanLiterals.ts, 26, 5)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) + +const bb6: Box<[boolean]> = box([false]); // Ok, Box<[boolean]> +>bb6 : Symbol(bb6, Decl(contextuallyTypedBooleanLiterals.ts, 27, 5)) +>Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) + +const bb7: Box<{ deep: { prop: boolean } }> = box({ deep: { prop: false } }); // Ok, Box<{ deep: { prop: boolean } }> +>bb7 : Symbol(bb7, Decl(contextuallyTypedBooleanLiterals.ts, 29, 5)) +>Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0)) +>deep : Symbol(deep, Decl(contextuallyTypedBooleanLiterals.ts, 29, 16)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 29, 24)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) +>deep : Symbol(deep, Decl(contextuallyTypedBooleanLiterals.ts, 29, 51)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 29, 59)) + +const bb8: Box<{ prop: true; other: string } | { prop: false; other: number }> = box({ prop: false, other: 0 }); // Error (T is invariant), Box<{ prop: false; other: number }> +>bb8 : Symbol(bb8, Decl(contextuallyTypedBooleanLiterals.ts, 31, 5)) +>Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 31, 16)) +>other : Symbol(other, Decl(contextuallyTypedBooleanLiterals.ts, 31, 28)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 31, 48)) +>other : Symbol(other, Decl(contextuallyTypedBooleanLiterals.ts, 31, 61)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 31, 86)) +>other : Symbol(other, Decl(contextuallyTypedBooleanLiterals.ts, 31, 99)) + +const bb9: Box = box(false); // Ok, Box +>bb9 : Symbol(bb9, Decl(contextuallyTypedBooleanLiterals.ts, 33, 5)) +>Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) + +const bb10: Box<{ prop: false }> = box({ prop: false }); // Ok, Box<{ prop: false }> +>bb10 : Symbol(bb10, Decl(contextuallyTypedBooleanLiterals.ts, 35, 5)) +>Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 35, 17)) +>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 2)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 35, 40)) + +type Box2 = { +>Box2 : Symbol(Box2, Decl(contextuallyTypedBooleanLiterals.ts, 35, 56)) +>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 37, 10)) + + get: () => T; +>get : Symbol(get, Decl(contextuallyTypedBooleanLiterals.ts, 37, 16)) +>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 37, 10)) + +}; + +const bb11: Box2<{ prop: true; other: string } | { prop: false; other: number }> = box2({ prop: false, other: 0 }); // Ok, Box2<{ prop: false; other: number }> +>bb11 : Symbol(bb11, Decl(contextuallyTypedBooleanLiterals.ts, 41, 5)) +>Box2 : Symbol(Box2, Decl(contextuallyTypedBooleanLiterals.ts, 35, 56)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 41, 18)) +>other : Symbol(other, Decl(contextuallyTypedBooleanLiterals.ts, 41, 30)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 41, 50)) +>other : Symbol(other, Decl(contextuallyTypedBooleanLiterals.ts, 41, 63)) +>box2 : Symbol(box2, Decl(contextuallyTypedBooleanLiterals.ts, 41, 115)) +>prop : Symbol(prop, Decl(contextuallyTypedBooleanLiterals.ts, 41, 89)) +>other : Symbol(other, Decl(contextuallyTypedBooleanLiterals.ts, 41, 102)) + +declare function box2(value: T): Box2; +>box2 : Symbol(box2, Decl(contextuallyTypedBooleanLiterals.ts, 41, 115)) +>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 43, 22)) +>value : Symbol(value, Decl(contextuallyTypedBooleanLiterals.ts, 43, 25)) +>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 43, 22)) +>Box2 : Symbol(Box2, Decl(contextuallyTypedBooleanLiterals.ts, 35, 56)) +>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 43, 22)) // Repro from #48150 interface Observable ->Observable : Symbol(Observable, Decl(contextuallyTypedBooleanLiterals.ts, 13, 37)) ->T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 17, 21)) +>Observable : Symbol(Observable, Decl(contextuallyTypedBooleanLiterals.ts, 43, 44)) +>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 47, 21)) { (): T; ->T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 17, 21)) +>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 47, 21)) (value: T): any; ->value : Symbol(value, Decl(contextuallyTypedBooleanLiterals.ts, 20, 3)) ->T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 17, 21)) +>value : Symbol(value, Decl(contextuallyTypedBooleanLiterals.ts, 50, 3)) +>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 47, 21)) } declare function observable(value: T): Observable; ->observable : Symbol(observable, Decl(contextuallyTypedBooleanLiterals.ts, 21, 1)) ->T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 23, 28)) ->value : Symbol(value, Decl(contextuallyTypedBooleanLiterals.ts, 23, 31)) ->T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 23, 28)) ->Observable : Symbol(Observable, Decl(contextuallyTypedBooleanLiterals.ts, 13, 37)) ->T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 23, 28)) +>observable : Symbol(observable, Decl(contextuallyTypedBooleanLiterals.ts, 51, 1)) +>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 53, 28)) +>value : Symbol(value, Decl(contextuallyTypedBooleanLiterals.ts, 53, 31)) +>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 53, 28)) +>Observable : Symbol(Observable, Decl(contextuallyTypedBooleanLiterals.ts, 43, 44)) +>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 53, 28)) const x: Observable = observable(false); ->x : Symbol(x, Decl(contextuallyTypedBooleanLiterals.ts, 25, 5)) ->Observable : Symbol(Observable, Decl(contextuallyTypedBooleanLiterals.ts, 13, 37)) ->observable : Symbol(observable, Decl(contextuallyTypedBooleanLiterals.ts, 21, 1)) +>x : Symbol(x, Decl(contextuallyTypedBooleanLiterals.ts, 55, 5)) +>Observable : Symbol(Observable, Decl(contextuallyTypedBooleanLiterals.ts, 43, 44)) +>observable : Symbol(observable, Decl(contextuallyTypedBooleanLiterals.ts, 51, 1)) + diff --git a/tests/baselines/reference/contextuallyTypedBooleanLiterals.types b/tests/baselines/reference/contextuallyTypedBooleanLiterals.types index bf9818ad6bd80..b028b1a77f4f4 100644 --- a/tests/baselines/reference/contextuallyTypedBooleanLiterals.types +++ b/tests/baselines/reference/contextuallyTypedBooleanLiterals.types @@ -7,16 +7,17 @@ type Box = { >Box : Box > : ^^^^^^ - get: () => T, + get: () => T; >get : () => T > : ^^^^^^ - set: (value: T) => void + set: (value: T) => void; >set : (value: T) => void > : ^ ^^ ^^^^^ >value : T > : ^ -} + +}; declare function box(value: T): Box; >box : (value: T) => Box @@ -24,7 +25,7 @@ declare function box(value: T): Box; >value : T > : ^ -const bn1 = box(0); // Box +const bn1 = box(0); // Box >bn1 : Box > : ^^^^^^^^^^^ >box(0) : Box @@ -34,7 +35,7 @@ const bn1 = box(0); // Box >0 : 0 > : ^ -const bn2: Box = box(0); // Ok +const bn2: Box = box(0); // Ok, Box >bn2 : Box > : ^^^^^^^^^^^ >box(0) : Box @@ -44,7 +45,7 @@ const bn2: Box = box(0); // Ok >0 : 0 > : ^ -const bb1 = box(false); // Box +const bb1 = box(false); // Box >bb1 : Box > : ^^^^^^^^^^^^ >box(false) : Box @@ -54,7 +55,7 @@ const bb1 = box(false); // Box >false : false > : ^^^^^ -const bb2: Box = box(false); // Error, box not assignable to Box +const bb2: Box = box(false); // Ok, Box >bb2 : Box > : ^^^^^^^^^^^^ >box(false) : Box @@ -64,6 +65,244 @@ const bb2: Box = box(false); // Error, box not assignable to Bo >false : false > : ^^^^^ +// https://github.com/microsoft/TypeScript/issues/59754 + +const bn3 = box({ prop: 0 }); // Box<{ prop: number }> +>bn3 : Box<{ prop: number; }> +> : ^^^^^^^^^^^^^^^^^^^^^^ +>box({ prop: 0 }) : Box<{ prop: number; }> +> : ^^^^^^^^^^^^^^^^^^^^^^ +>box : (value: T) => Box +> : ^ ^^ ^^ ^^^^^ +>{ prop: 0 } : { prop: number; } +> : ^^^^^^^^^^^^^^^^^ +>prop : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const bn4: Box<{ prop: number }> = box({ prop: 0 }); // Ok, Box<{ prop: number }> +>bn4 : Box<{ prop: number; }> +> : ^^^^^^^^^^^^ ^^^^ +>prop : number +> : ^^^^^^ +>box({ prop: 0 }) : Box<{ prop: number; }> +> : ^^^^^^^^^^^^^^^^^^^^^^ +>box : (value: T) => Box +> : ^ ^^ ^^ ^^^^^ +>{ prop: 0 } : { prop: number; } +> : ^^^^^^^^^^^^^^^^^ +>prop : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const bb3 = box({ prop: false }); // Box +>bb3 : Box<{ prop: boolean; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^ +>box({ prop: false }) : Box<{ prop: boolean; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^ +>box : (value: T) => Box +> : ^ ^^ ^^ ^^^^^ +>{ prop: false } : { prop: false; } +> : ^^^^^^^^^^^^^^^^ +>prop : false +> : ^^^^^ +>false : false +> : ^^^^^ + +const bb4: Box<{ prop: boolean }> = box({ prop: false }); // Ok, Box<{ prop: boolean }> +>bb4 : Box<{ prop: boolean; }> +> : ^^^^^^^^^^^^ ^^^^ +>prop : boolean +> : ^^^^^^^ +>box({ prop: false }) : Box<{ prop: boolean; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^ +>box : (value: T) => Box +> : ^ ^^ ^^ ^^^^^ +>{ prop: false } : { prop: false; } +> : ^^^^^^^^^^^^^^^^ +>prop : false +> : ^^^^^ +>false : false +> : ^^^^^ + +const bn5 = box([0]); // Box<[number]> +>bn5 : Box +> : ^^^^^^^^^^^^^ +>box([0]) : Box +> : ^^^^^^^^^^^^^ +>box : (value: T) => Box +> : ^ ^^ ^^ ^^^^^ +>[0] : number[] +> : ^^^^^^^^ +>0 : 0 +> : ^ + +const bn6: Box<[number]> = box([0]); // Ok, Box<[number]> +>bn6 : Box<[number]> +> : ^^^^^^^^^^^^^ +>box([0]) : Box<[number]> +> : ^^^^^^^^^^^^^ +>box : (value: T) => Box +> : ^ ^^ ^^ ^^^^^ +>[0] : [number] +> : ^^^^^^^^ +>0 : 0 +> : ^ + +const bb5 = box([false]); // Box<[boolean]> +>bb5 : Box +> : ^^^^^^^^^^^^^^ +>box([false]) : Box +> : ^^^^^^^^^^^^^^ +>box : (value: T) => Box +> : ^ ^^ ^^ ^^^^^ +>[false] : false[] +> : ^^^^^^^ +>false : false +> : ^^^^^ + +const bb6: Box<[boolean]> = box([false]); // Ok, Box<[boolean]> +>bb6 : Box<[boolean]> +> : ^^^^^^^^^^^^^^ +>box([false]) : Box<[boolean]> +> : ^^^^^^^^^^^^^^ +>box : (value: T) => Box +> : ^ ^^ ^^ ^^^^^ +>[false] : [false] +> : ^^^^^^^ +>false : false +> : ^^^^^ + +const bb7: Box<{ deep: { prop: boolean } }> = box({ deep: { prop: false } }); // Ok, Box<{ deep: { prop: boolean } }> +>bb7 : Box<{ deep: { prop: boolean; }; }> +> : ^^^^^^^^^^^^ ^^^^ +>deep : { prop: boolean; } +> : ^^^^^^^^ ^^^ +>prop : boolean +> : ^^^^^^^ +>box({ deep: { prop: false } }) : Box<{ deep: { prop: boolean; }; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>box : (value: T) => Box +> : ^ ^^ ^^ ^^^^^ +>{ deep: { prop: false } } : { deep: { prop: false; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>deep : { prop: false; } +> : ^^^^^^^^^^^^^^^^ +>{ prop: false } : { prop: false; } +> : ^^^^^^^^^^^^^^^^ +>prop : false +> : ^^^^^ +>false : false +> : ^^^^^ + +const bb8: Box<{ prop: true; other: string } | { prop: false; other: number }> = box({ prop: false, other: 0 }); // Error (T is invariant), Box<{ prop: false; other: number }> +>bb8 : Box<{ prop: true; other: string; } | { prop: false; other: number; }> +> : ^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^ +>prop : true +> : ^^^^ +>true : true +> : ^^^^ +>other : string +> : ^^^^^^ +>prop : false +> : ^^^^^ +>false : false +> : ^^^^^ +>other : number +> : ^^^^^^ +>box({ prop: false, other: 0 }) : Box<{ prop: false; other: number; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>box : (value: T) => Box +> : ^ ^^ ^^ ^^^^^ +>{ prop: false, other: 0 } : { prop: false; other: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>prop : false +> : ^^^^^ +>false : false +> : ^^^^^ +>other : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const bb9: Box = box(false); // Ok, Box +>bb9 : Box +> : ^^^^^^^^^^ +>false : false +> : ^^^^^ +>box(false) : Box +> : ^^^^^^^^^^ +>box : (value: T) => Box +> : ^ ^^ ^^ ^^^^^ +>false : false +> : ^^^^^ + +const bb10: Box<{ prop: false }> = box({ prop: false }); // Ok, Box<{ prop: false }> +>bb10 : Box<{ prop: false; }> +> : ^^^^^^^^^^^^ ^^^^ +>prop : false +> : ^^^^^ +>false : false +> : ^^^^^ +>box({ prop: false }) : Box<{ prop: false; }> +> : ^^^^^^^^^^^^^^^^^^^^^ +>box : (value: T) => Box +> : ^ ^^ ^^ ^^^^^ +>{ prop: false } : { prop: false; } +> : ^^^^^^^^^^^^^^^^ +>prop : false +> : ^^^^^ +>false : false +> : ^^^^^ + +type Box2 = { +>Box2 : Box2 +> : ^^^^^^^ + + get: () => T; +>get : () => T +> : ^^^^^^ + +}; + +const bb11: Box2<{ prop: true; other: string } | { prop: false; other: number }> = box2({ prop: false, other: 0 }); // Ok, Box2<{ prop: false; other: number }> +>bb11 : Box2<{ prop: true; other: string; } | { prop: false; other: number; }> +> : ^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^ +>prop : true +> : ^^^^ +>true : true +> : ^^^^ +>other : string +> : ^^^^^^ +>prop : false +> : ^^^^^ +>false : false +> : ^^^^^ +>other : number +> : ^^^^^^ +>box2({ prop: false, other: 0 }) : Box2<{ prop: false; other: number; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>box2 : (value: T) => Box2 +> : ^ ^^ ^^ ^^^^^ +>{ prop: false, other: 0 } : { prop: false; other: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>prop : false +> : ^^^^^ +>false : false +> : ^^^^^ +>other : number +> : ^^^^^^ +>0 : 0 +> : ^ + +declare function box2(value: T): Box2; +>box2 : (value: T) => Box2 +> : ^ ^^ ^^ ^^^^^ +>value : T +> : ^ + // Repro from #48150 interface Observable @@ -90,3 +329,4 @@ const x: Observable = observable(false); >false : false > : ^^^^^ + diff --git a/tests/cases/compiler/contextuallyTypedBooleanLiterals.ts b/tests/cases/compiler/contextuallyTypedBooleanLiterals.ts index 8cbe5f64def4b..1537c27ae4cc7 100644 --- a/tests/cases/compiler/contextuallyTypedBooleanLiterals.ts +++ b/tests/cases/compiler/contextuallyTypedBooleanLiterals.ts @@ -7,17 +7,47 @@ // Repro from #48363 type Box = { - get: () => T, - set: (value: T) => void -} + get: () => T; + set: (value: T) => void; +}; declare function box(value: T): Box; -const bn1 = box(0); // Box -const bn2: Box = box(0); // Ok +const bn1 = box(0); // Box +const bn2: Box = box(0); // Ok, Box + +const bb1 = box(false); // Box +const bb2: Box = box(false); // Ok, Box + +// https://github.com/microsoft/TypeScript/issues/59754 + +const bn3 = box({ prop: 0 }); // Box<{ prop: number }> +const bn4: Box<{ prop: number }> = box({ prop: 0 }); // Ok, Box<{ prop: number }> + +const bb3 = box({ prop: false }); // Box +const bb4: Box<{ prop: boolean }> = box({ prop: false }); // Ok, Box<{ prop: boolean }> + +const bn5 = box([0]); // Box<[number]> +const bn6: Box<[number]> = box([0]); // Ok, Box<[number]> + +const bb5 = box([false]); // Box<[boolean]> +const bb6: Box<[boolean]> = box([false]); // Ok, Box<[boolean]> -const bb1 = box(false); // Box -const bb2: Box = box(false); // Error, box not assignable to Box +const bb7: Box<{ deep: { prop: boolean } }> = box({ deep: { prop: false } }); // Ok, Box<{ deep: { prop: boolean } }> + +const bb8: Box<{ prop: true; other: string } | { prop: false; other: number }> = box({ prop: false, other: 0 }); // Error (T is invariant), Box<{ prop: false; other: number }> + +const bb9: Box = box(false); // Ok, Box + +const bb10: Box<{ prop: false }> = box({ prop: false }); // Ok, Box<{ prop: false }> + +type Box2 = { + get: () => T; +}; + +const bb11: Box2<{ prop: true; other: string } | { prop: false; other: number }> = box2({ prop: false, other: 0 }); // Ok, Box2<{ prop: false; other: number }> + +declare function box2(value: T): Box2; // Repro from #48150 @@ -30,3 +60,4 @@ interface Observable declare function observable(value: T): Observable; const x: Observable = observable(false); +