From ba7033b5675253b5dbffd29bb80f94f215521023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 14 Dec 2023 09:39:11 +0100 Subject: [PATCH] Disallow more flavors of property accesses on `never` in expression space --- src/compiler/checker.ts | 6 +- .../arrayDestructuringInSwitch2.types | 2 +- ...ntrolFlowAssignmentPatternOrder.errors.txt | 97 +++++++++++++++++++ .../objectDestructuringInSwitch1.errors.txt | 20 ++++ .../objectDestructuringInSwitch1.symbols | 41 ++++++++ .../objectDestructuringInSwitch1.types | 46 +++++++++ ...rameterBindingPatternWithNever1.errors.txt | 14 +++ .../parameterBindingPatternWithNever1.symbols | 20 ++++ .../parameterBindingPatternWithNever1.types | 20 ++++ .../propertyAccessOnNever1.errors.txt | 22 +++++ .../reference/propertyAccessOnNever1.symbols | 18 ++++ .../reference/propertyAccessOnNever1.types | 22 +++++ .../compiler/objectDestructuringInSwitch1.ts | 17 ++++ .../parameterBindingPatternWithNever1.ts | 10 ++ .../cases/compiler/propertyAccessOnNever1.ts | 10 ++ 15 files changed, 361 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/controlFlowAssignmentPatternOrder.errors.txt create mode 100644 tests/baselines/reference/objectDestructuringInSwitch1.errors.txt create mode 100644 tests/baselines/reference/objectDestructuringInSwitch1.symbols create mode 100644 tests/baselines/reference/objectDestructuringInSwitch1.types create mode 100644 tests/baselines/reference/parameterBindingPatternWithNever1.errors.txt create mode 100644 tests/baselines/reference/parameterBindingPatternWithNever1.symbols create mode 100644 tests/baselines/reference/parameterBindingPatternWithNever1.types create mode 100644 tests/baselines/reference/propertyAccessOnNever1.errors.txt create mode 100644 tests/baselines/reference/propertyAccessOnNever1.symbols create mode 100644 tests/baselines/reference/propertyAccessOnNever1.types create mode 100644 tests/cases/compiler/objectDestructuringInSwitch1.ts create mode 100644 tests/cases/compiler/parameterBindingPatternWithNever1.ts create mode 100644 tests/cases/compiler/propertyAccessOnNever1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cd89bbf688a53..9e42037f899ef 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10863,7 +10863,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { mapType(baseConstraint, t => sliceTupleType(t as TupleTypeReference, index)) : createArrayType(elementType); } - else if (isArrayLikeType(parentType)) { + else if (isArrayLikeType(parentType) && !isErrorType(elementType)) { const indexType = getNumberLiteralType(index); const accessFlags = AccessFlags.ExpressionPosition | (noTupleBoundsCheck || hasDefaultValue(declaration) ? AccessFlags.NoTupleBoundsCheck : 0); const declaredType = getIndexedAccessTypeOrUndefined(parentType, indexType, accessFlags, declaration.name) || errorType; @@ -18040,7 +18040,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } if (!(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike)) { - if (objectType.flags & (TypeFlags.Any | TypeFlags.Never)) { + if (objectType.flags & TypeFlags.Any || objectType.flags & TypeFlags.Never && !(accessFlags & AccessFlags.ExpressionPosition)) { return objectType; } // If no index signature is applicable, we default to the string index signature. In effect, this means the string @@ -42893,7 +42893,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const allowAsyncIterables = (use & IterationUse.AllowsAsyncIterablesFlag) !== 0; if (inputType === neverType) { reportTypeNotIterableError(errorNode!, inputType, allowAsyncIterables); // TODO: GH#18217 - return undefined; + return errorType; } const uplevelIteration = languageVersion >= ScriptTarget.ES2015; diff --git a/tests/baselines/reference/arrayDestructuringInSwitch2.types b/tests/baselines/reference/arrayDestructuringInSwitch2.types index 759e2f1b664d8..52ae6f80f66e5 100644 --- a/tests/baselines/reference/arrayDestructuringInSwitch2.types +++ b/tests/baselines/reference/arrayDestructuringInSwitch2.types @@ -36,7 +36,7 @@ function foo(x: X): 1 { default: const [n] = a; ->n : never +>n : any >a : never return a; diff --git a/tests/baselines/reference/controlFlowAssignmentPatternOrder.errors.txt b/tests/baselines/reference/controlFlowAssignmentPatternOrder.errors.txt new file mode 100644 index 0000000000000..ea7429a9d0f30 --- /dev/null +++ b/tests/baselines/reference/controlFlowAssignmentPatternOrder.errors.txt @@ -0,0 +1,97 @@ +controlFlowAssignmentPatternOrder.ts(6,9): error TS2339: Property '1' does not exist on type 'never'. +controlFlowAssignmentPatternOrder.ts(12,9): error TS2339: Property '0' does not exist on type 'never'. +controlFlowAssignmentPatternOrder.ts(31,9): error TS2339: Property '1' does not exist on type 'never'. +controlFlowAssignmentPatternOrder.ts(37,9): error TS2339: Property '0' does not exist on type 'never'. +controlFlowAssignmentPatternOrder.ts(56,14): error TS2339: Property '1' does not exist on type 'never'. +controlFlowAssignmentPatternOrder.ts(62,14): error TS2339: Property '0' does not exist on type 'never'. + + +==== controlFlowAssignmentPatternOrder.ts (6 errors) ==== + // https://github.com/microsoft/TypeScript/pull/41094#issuecomment-716044363 + declare function f(): void; + { + let a: 0 | 1 = 0; + let b: 0 | 1 | 9; + [{ [(a = 1)]: b } = [9, a] as const] = []; + ~~~~~~~ +!!! error TS2339: Property '1' does not exist on type 'never'. + const bb: 0 = b; + } + { + let a: 0 | 1 = 1; + let b: 0 | 1 | 9; + [{ [a]: b } = [9, a = 0] as const] = []; + ~ +!!! error TS2339: Property '0' does not exist on type 'never'. + const bb: 9 = b; + } + { + let a: 0 | 1 = 0; + let b: 0 | 1 | 8 | 9; + [{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] as const]; + const bb: 0 | 8 = b; + } + { + let a: 0 | 1 = 1; + let b: 0 | 1 | 8 | 9; + [{ [a]: b } = [a = 0, 9] as const] = [[8, 9] as const]; + const bb: 0 | 8 = b; + } + // same as above but on left of a binary expression + { + let a: 0 | 1 = 0; + let b: 0 | 1 | 9; + [{ [(a = 1)]: b } = [9, a] as const] = [], f(); + ~~~~~~~ +!!! error TS2339: Property '1' does not exist on type 'never'. + const bb: 0 = b; + } + { + let a: 0 | 1 = 1; + let b: 0 | 1 | 9; + [{ [a]: b } = [9, a = 0] as const] = [], f(); + ~ +!!! error TS2339: Property '0' does not exist on type 'never'. + const bb: 9 = b; + } + { + let a: 0 | 1 = 0; + let b: 0 | 1 | 8 | 9; + [{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] as const], f(); + const bb: 0 | 8 = b; + } + { + let a: 0 | 1 = 1; + let b: 0 | 1 | 8 | 9; + [{ [a]: b } = [a = 0, 9] as const] = [[8, 9] as const], f(); + const bb: 0 | 8 = b; + } + // same as above but on right of a binary expression + { + let a: 0 | 1 = 0; + let b: 0 | 1 | 9; + f(), [{ [(a = 1)]: b } = [9, a] as const] = []; + ~~~~~~~ +!!! error TS2339: Property '1' does not exist on type 'never'. + const bb: 0 = b; + } + { + let a: 0 | 1 = 1; + let b: 0 | 1 | 9; + f(), [{ [a]: b } = [9, a = 0] as const] = []; + ~ +!!! error TS2339: Property '0' does not exist on type 'never'. + const bb: 9 = b; + } + { + let a: 0 | 1 = 0; + let b: 0 | 1 | 8 | 9; + f(), [{ [(a = 1)]: b } = [9, a] as const] = [[9, 8] as const]; + const bb: 0 | 8 = b; + } + { + let a: 0 | 1 = 1; + let b: 0 | 1 | 8 | 9; + f(), [{ [a]: b } = [a = 0, 9] as const] = [[8, 9] as const]; + const bb: 0 | 8 = b; + } \ No newline at end of file diff --git a/tests/baselines/reference/objectDestructuringInSwitch1.errors.txt b/tests/baselines/reference/objectDestructuringInSwitch1.errors.txt new file mode 100644 index 0000000000000..70c7216b6779c --- /dev/null +++ b/tests/baselines/reference/objectDestructuringInSwitch1.errors.txt @@ -0,0 +1,20 @@ +objectDestructuringInSwitch1.ts(11,15): error TS2339: Property 'prop' does not exist on type 'never'. + + +==== objectDestructuringInSwitch1.ts (1 errors) ==== + type X = { kind: "a", a: { prop: 1 } } | { kind: "b", a: {} } + + function foo(x: X): 1 { + const { kind, a } = x; + switch (kind) { + case "a": + return a.prop; + case "b": + return 1; + default: + const { prop } = a; + ~~~~ +!!! error TS2339: Property 'prop' does not exist on type 'never'. + return a; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/objectDestructuringInSwitch1.symbols b/tests/baselines/reference/objectDestructuringInSwitch1.symbols new file mode 100644 index 0000000000000..d0a947786dc81 --- /dev/null +++ b/tests/baselines/reference/objectDestructuringInSwitch1.symbols @@ -0,0 +1,41 @@ +//// [tests/cases/compiler/objectDestructuringInSwitch1.ts] //// + +=== objectDestructuringInSwitch1.ts === +type X = { kind: "a", a: { prop: 1 } } | { kind: "b", a: {} } +>X : Symbol(X, Decl(objectDestructuringInSwitch1.ts, 0, 0)) +>kind : Symbol(kind, Decl(objectDestructuringInSwitch1.ts, 0, 10)) +>a : Symbol(a, Decl(objectDestructuringInSwitch1.ts, 0, 21)) +>prop : Symbol(prop, Decl(objectDestructuringInSwitch1.ts, 0, 26)) +>kind : Symbol(kind, Decl(objectDestructuringInSwitch1.ts, 0, 42)) +>a : Symbol(a, Decl(objectDestructuringInSwitch1.ts, 0, 53)) + +function foo(x: X): 1 { +>foo : Symbol(foo, Decl(objectDestructuringInSwitch1.ts, 0, 61)) +>x : Symbol(x, Decl(objectDestructuringInSwitch1.ts, 2, 13)) +>X : Symbol(X, Decl(objectDestructuringInSwitch1.ts, 0, 0)) + + const { kind, a } = x; +>kind : Symbol(kind, Decl(objectDestructuringInSwitch1.ts, 3, 9)) +>a : Symbol(a, Decl(objectDestructuringInSwitch1.ts, 3, 15)) +>x : Symbol(x, Decl(objectDestructuringInSwitch1.ts, 2, 13)) + + switch (kind) { +>kind : Symbol(kind, Decl(objectDestructuringInSwitch1.ts, 3, 9)) + + case "a": + return a.prop; +>a.prop : Symbol(prop, Decl(objectDestructuringInSwitch1.ts, 0, 26)) +>a : Symbol(a, Decl(objectDestructuringInSwitch1.ts, 3, 15)) +>prop : Symbol(prop, Decl(objectDestructuringInSwitch1.ts, 0, 26)) + + case "b": + return 1; + default: + const { prop } = a; +>prop : Symbol(prop, Decl(objectDestructuringInSwitch1.ts, 10, 13)) +>a : Symbol(a, Decl(objectDestructuringInSwitch1.ts, 3, 15)) + + return a; +>a : Symbol(a, Decl(objectDestructuringInSwitch1.ts, 3, 15)) + } +} diff --git a/tests/baselines/reference/objectDestructuringInSwitch1.types b/tests/baselines/reference/objectDestructuringInSwitch1.types new file mode 100644 index 0000000000000..d4ee0471bbac6 --- /dev/null +++ b/tests/baselines/reference/objectDestructuringInSwitch1.types @@ -0,0 +1,46 @@ +//// [tests/cases/compiler/objectDestructuringInSwitch1.ts] //// + +=== objectDestructuringInSwitch1.ts === +type X = { kind: "a", a: { prop: 1 } } | { kind: "b", a: {} } +>X : { kind: "a"; a: { prop: 1;}; } | { kind: "b"; a: {}; } +>kind : "a" +>a : { prop: 1; } +>prop : 1 +>kind : "b" +>a : {} + +function foo(x: X): 1 { +>foo : (x: X) => 1 +>x : X + + const { kind, a } = x; +>kind : "a" | "b" +>a : {} | { prop: 1; } +>x : X + + switch (kind) { +>kind : "a" | "b" + + case "a": +>"a" : "a" + + return a.prop; +>a.prop : 1 +>a : { prop: 1; } +>prop : 1 + + case "b": +>"b" : "b" + + return 1; +>1 : 1 + + default: + const { prop } = a; +>prop : any +>a : never + + return a; +>a : never + } +} diff --git a/tests/baselines/reference/parameterBindingPatternWithNever1.errors.txt b/tests/baselines/reference/parameterBindingPatternWithNever1.errors.txt new file mode 100644 index 0000000000000..698ed78eeca24 --- /dev/null +++ b/tests/baselines/reference/parameterBindingPatternWithNever1.errors.txt @@ -0,0 +1,14 @@ +parameterBindingPatternWithNever1.ts(5,29): error TS2339: Property 'foo' does not exist on type 'never'. + + +==== parameterBindingPatternWithNever1.ts (1 errors) ==== + function something(foo: string) {} + + type ComplexTypeThatReturnsNever = never; + + function somethingWrapper({ foo }: ComplexTypeThatReturnsNever) { + ~~~ +!!! error TS2339: Property 'foo' does not exist on type 'never'. + something(foo); + } + \ No newline at end of file diff --git a/tests/baselines/reference/parameterBindingPatternWithNever1.symbols b/tests/baselines/reference/parameterBindingPatternWithNever1.symbols new file mode 100644 index 0000000000000..6f467732f513b --- /dev/null +++ b/tests/baselines/reference/parameterBindingPatternWithNever1.symbols @@ -0,0 +1,20 @@ +//// [tests/cases/compiler/parameterBindingPatternWithNever1.ts] //// + +=== parameterBindingPatternWithNever1.ts === +function something(foo: string) {} +>something : Symbol(something, Decl(parameterBindingPatternWithNever1.ts, 0, 0)) +>foo : Symbol(foo, Decl(parameterBindingPatternWithNever1.ts, 0, 19)) + +type ComplexTypeThatReturnsNever = never; +>ComplexTypeThatReturnsNever : Symbol(ComplexTypeThatReturnsNever, Decl(parameterBindingPatternWithNever1.ts, 0, 34)) + +function somethingWrapper({ foo }: ComplexTypeThatReturnsNever) { +>somethingWrapper : Symbol(somethingWrapper, Decl(parameterBindingPatternWithNever1.ts, 2, 41)) +>foo : Symbol(foo, Decl(parameterBindingPatternWithNever1.ts, 4, 27)) +>ComplexTypeThatReturnsNever : Symbol(ComplexTypeThatReturnsNever, Decl(parameterBindingPatternWithNever1.ts, 0, 34)) + + something(foo); +>something : Symbol(something, Decl(parameterBindingPatternWithNever1.ts, 0, 0)) +>foo : Symbol(foo, Decl(parameterBindingPatternWithNever1.ts, 4, 27)) +} + diff --git a/tests/baselines/reference/parameterBindingPatternWithNever1.types b/tests/baselines/reference/parameterBindingPatternWithNever1.types new file mode 100644 index 0000000000000..57483f68cee5e --- /dev/null +++ b/tests/baselines/reference/parameterBindingPatternWithNever1.types @@ -0,0 +1,20 @@ +//// [tests/cases/compiler/parameterBindingPatternWithNever1.ts] //// + +=== parameterBindingPatternWithNever1.ts === +function something(foo: string) {} +>something : (foo: string) => void +>foo : string + +type ComplexTypeThatReturnsNever = never; +>ComplexTypeThatReturnsNever : never + +function somethingWrapper({ foo }: ComplexTypeThatReturnsNever) { +>somethingWrapper : ({ foo }: ComplexTypeThatReturnsNever) => void +>foo : any + + something(foo); +>something(foo) : void +>something : (foo: string) => void +>foo : any +} + diff --git a/tests/baselines/reference/propertyAccessOnNever1.errors.txt b/tests/baselines/reference/propertyAccessOnNever1.errors.txt new file mode 100644 index 0000000000000..8bbd155cf2674 --- /dev/null +++ b/tests/baselines/reference/propertyAccessOnNever1.errors.txt @@ -0,0 +1,22 @@ +propertyAccessOnNever1.ts(5,9): error TS2339: Property 'foo' does not exist on type 'never'. +propertyAccessOnNever1.ts(6,1): error TS7053: Element implicitly has an 'any' type because expression of type '"foo"' can't be used to index type 'never'. + Property 'foo' does not exist on type 'never'. +propertyAccessOnNever1.ts(7,9): error TS2339: Property 'foo' does not exist on type 'never'. + + +==== propertyAccessOnNever1.ts (3 errors) ==== + // https://github.com/microsoft/TypeScript/issues/56778 + + declare const example: never; + + example.foo; + ~~~ +!!! error TS2339: Property 'foo' does not exist on type 'never'. + example['foo']; + ~~~~~~~~~~~~~~ +!!! error TS7053: Element implicitly has an 'any' type because expression of type '"foo"' can't be used to index type 'never'. +!!! error TS7053: Property 'foo' does not exist on type 'never'. + const { foo } = example + ~~~ +!!! error TS2339: Property 'foo' does not exist on type 'never'. + \ No newline at end of file diff --git a/tests/baselines/reference/propertyAccessOnNever1.symbols b/tests/baselines/reference/propertyAccessOnNever1.symbols new file mode 100644 index 0000000000000..19b05dda9fe35 --- /dev/null +++ b/tests/baselines/reference/propertyAccessOnNever1.symbols @@ -0,0 +1,18 @@ +//// [tests/cases/compiler/propertyAccessOnNever1.ts] //// + +=== propertyAccessOnNever1.ts === +// https://github.com/microsoft/TypeScript/issues/56778 + +declare const example: never; +>example : Symbol(example, Decl(propertyAccessOnNever1.ts, 2, 13)) + +example.foo; +>example : Symbol(example, Decl(propertyAccessOnNever1.ts, 2, 13)) + +example['foo']; +>example : Symbol(example, Decl(propertyAccessOnNever1.ts, 2, 13)) + +const { foo } = example +>foo : Symbol(foo, Decl(propertyAccessOnNever1.ts, 6, 7)) +>example : Symbol(example, Decl(propertyAccessOnNever1.ts, 2, 13)) + diff --git a/tests/baselines/reference/propertyAccessOnNever1.types b/tests/baselines/reference/propertyAccessOnNever1.types new file mode 100644 index 0000000000000..81ecdbc3f4ac3 --- /dev/null +++ b/tests/baselines/reference/propertyAccessOnNever1.types @@ -0,0 +1,22 @@ +//// [tests/cases/compiler/propertyAccessOnNever1.ts] //// + +=== propertyAccessOnNever1.ts === +// https://github.com/microsoft/TypeScript/issues/56778 + +declare const example: never; +>example : never + +example.foo; +>example.foo : any +>example : never +>foo : any + +example['foo']; +>example['foo'] : any +>example : never +>'foo' : "foo" + +const { foo } = example +>foo : any +>example : never + diff --git a/tests/cases/compiler/objectDestructuringInSwitch1.ts b/tests/cases/compiler/objectDestructuringInSwitch1.ts new file mode 100644 index 0000000000000..333cd35113526 --- /dev/null +++ b/tests/cases/compiler/objectDestructuringInSwitch1.ts @@ -0,0 +1,17 @@ +// @strict: true +// @noEmit: true + +type X = { kind: "a", a: { prop: 1 } } | { kind: "b", a: {} } + +function foo(x: X): 1 { + const { kind, a } = x; + switch (kind) { + case "a": + return a.prop; + case "b": + return 1; + default: + const { prop } = a; + return a; + } +} \ No newline at end of file diff --git a/tests/cases/compiler/parameterBindingPatternWithNever1.ts b/tests/cases/compiler/parameterBindingPatternWithNever1.ts new file mode 100644 index 0000000000000..cebd5e7917d4b --- /dev/null +++ b/tests/cases/compiler/parameterBindingPatternWithNever1.ts @@ -0,0 +1,10 @@ +// @strict: true +// @noEmit: true + +function something(foo: string) {} + +type ComplexTypeThatReturnsNever = never; + +function somethingWrapper({ foo }: ComplexTypeThatReturnsNever) { + something(foo); +} diff --git a/tests/cases/compiler/propertyAccessOnNever1.ts b/tests/cases/compiler/propertyAccessOnNever1.ts new file mode 100644 index 0000000000000..c7149e2733691 --- /dev/null +++ b/tests/cases/compiler/propertyAccessOnNever1.ts @@ -0,0 +1,10 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/56778 + +declare const example: never; + +example.foo; +example['foo']; +const { foo } = example