From fd28dbdddec2fcd8ab14449f6a72af9755658232 Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 17 Oct 2019 18:19:22 +0800 Subject: [PATCH] fix control flow in switch case --- src/compiler/checker.ts | 4 ++ .../reference/controlFlowOptionalChain1.js | 32 ++++++++++ .../controlFlowOptionalChain1.symbols | 52 ++++++++++++++++ .../reference/controlFlowOptionalChain1.types | 60 +++++++++++++++++++ .../controlFlow/controlFlowOptionalChain1.ts | 18 ++++++ 5 files changed, 166 insertions(+) create mode 100644 tests/baselines/reference/controlFlowOptionalChain1.js create mode 100644 tests/baselines/reference/controlFlowOptionalChain1.symbols create mode 100644 tests/baselines/reference/controlFlowOptionalChain1.types create mode 100644 tests/cases/conformance/controlFlow/controlFlowOptionalChain1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b1c206def3ce4..baa1eb15dce09 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19324,6 +19324,10 @@ namespace ts { if (propName === undefined) { return type; } + if (strictNullChecks && access.questionDotToken) { + type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) + } + const propType = getTypeOfPropertyOfType(type, propName); const narrowedPropType = propType && narrowType(propType); return propType === narrowedPropType ? type : filterType(type, t => isTypeComparableTo(getTypeOfPropertyOrIndexSignature(t, propName), narrowedPropType!)); diff --git a/tests/baselines/reference/controlFlowOptionalChain1.js b/tests/baselines/reference/controlFlowOptionalChain1.js new file mode 100644 index 0000000000000..2bc75e0d82d60 --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain1.js @@ -0,0 +1,32 @@ +//// [controlFlowOptionalChain1.ts] +type Shape = + | { type: 'rectangle', width: number, height: number } + | { type: 'circle', radius: number } + +declare function assertUndefined(v: undefined): void + +function getArea(shape?: Shape) { + switch (shape?.type) { + case 'circle': + return Math.PI * shape.radius ** 2 + case 'rectangle': + return shape.width * shape.height + default: + return assertUndefined(shape) + } +} + + +//// [controlFlowOptionalChain1.js] +"use strict"; +function getArea(shape) { + var _a; + switch ((_a = shape) === null || _a === void 0 ? void 0 : _a.type) { + case 'circle': + return Math.PI * Math.pow(shape.radius, 2); + case 'rectangle': + return shape.width * shape.height; + default: + return assertUndefined(shape); + } +} diff --git a/tests/baselines/reference/controlFlowOptionalChain1.symbols b/tests/baselines/reference/controlFlowOptionalChain1.symbols new file mode 100644 index 0000000000000..f1973276de9e8 --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain1.symbols @@ -0,0 +1,52 @@ +=== tests/cases/conformance/controlFlow/controlFlowOptionalChain1.ts === +type Shape = +>Shape : Symbol(Shape, Decl(controlFlowOptionalChain1.ts, 0, 0)) + + | { type: 'rectangle', width: number, height: number } +>type : Symbol(type, Decl(controlFlowOptionalChain1.ts, 1, 7)) +>width : Symbol(width, Decl(controlFlowOptionalChain1.ts, 1, 26)) +>height : Symbol(height, Decl(controlFlowOptionalChain1.ts, 1, 41)) + + | { type: 'circle', radius: number } +>type : Symbol(type, Decl(controlFlowOptionalChain1.ts, 2, 7)) +>radius : Symbol(radius, Decl(controlFlowOptionalChain1.ts, 2, 23)) + +declare function assertUndefined(v: undefined): void +>assertUndefined : Symbol(assertUndefined, Decl(controlFlowOptionalChain1.ts, 2, 40)) +>v : Symbol(v, Decl(controlFlowOptionalChain1.ts, 4, 33)) + +function getArea(shape?: Shape) { +>getArea : Symbol(getArea, Decl(controlFlowOptionalChain1.ts, 4, 52)) +>shape : Symbol(shape, Decl(controlFlowOptionalChain1.ts, 6, 17)) +>Shape : Symbol(Shape, Decl(controlFlowOptionalChain1.ts, 0, 0)) + + switch (shape?.type) { +>shape?.type : Symbol(type, Decl(controlFlowOptionalChain1.ts, 1, 7), Decl(controlFlowOptionalChain1.ts, 2, 7)) +>shape : Symbol(shape, Decl(controlFlowOptionalChain1.ts, 6, 17)) +>type : Symbol(type, Decl(controlFlowOptionalChain1.ts, 1, 7), Decl(controlFlowOptionalChain1.ts, 2, 7)) + + case 'circle': + return Math.PI * shape.radius ** 2 +>Math.PI : Symbol(Math.PI, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.es5.d.ts, --, --)) +>shape.radius : Symbol(radius, Decl(controlFlowOptionalChain1.ts, 2, 23)) +>shape : Symbol(shape, Decl(controlFlowOptionalChain1.ts, 6, 17)) +>radius : Symbol(radius, Decl(controlFlowOptionalChain1.ts, 2, 23)) + + case 'rectangle': + return shape.width * shape.height +>shape.width : Symbol(width, Decl(controlFlowOptionalChain1.ts, 1, 26)) +>shape : Symbol(shape, Decl(controlFlowOptionalChain1.ts, 6, 17)) +>width : Symbol(width, Decl(controlFlowOptionalChain1.ts, 1, 26)) +>shape.height : Symbol(height, Decl(controlFlowOptionalChain1.ts, 1, 41)) +>shape : Symbol(shape, Decl(controlFlowOptionalChain1.ts, 6, 17)) +>height : Symbol(height, Decl(controlFlowOptionalChain1.ts, 1, 41)) + + default: + return assertUndefined(shape) +>assertUndefined : Symbol(assertUndefined, Decl(controlFlowOptionalChain1.ts, 2, 40)) +>shape : Symbol(shape, Decl(controlFlowOptionalChain1.ts, 6, 17)) + } +} + diff --git a/tests/baselines/reference/controlFlowOptionalChain1.types b/tests/baselines/reference/controlFlowOptionalChain1.types new file mode 100644 index 0000000000000..2db1679882666 --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain1.types @@ -0,0 +1,60 @@ +=== tests/cases/conformance/controlFlow/controlFlowOptionalChain1.ts === +type Shape = +>Shape : Shape + + | { type: 'rectangle', width: number, height: number } +>type : "rectangle" +>width : number +>height : number + + | { type: 'circle', radius: number } +>type : "circle" +>radius : number + +declare function assertUndefined(v: undefined): void +>assertUndefined : (v: undefined) => void +>v : undefined + +function getArea(shape?: Shape) { +>getArea : (shape?: { type: "rectangle"; width: number; height: number; } | { type: "circle"; radius: number; } | undefined) => number | void +>shape : { type: "rectangle"; width: number; height: number; } | { type: "circle"; radius: number; } | undefined + + switch (shape?.type) { +>shape?.type : "rectangle" | "circle" | undefined +>shape : { type: "rectangle"; width: number; height: number; } | { type: "circle"; radius: number; } | undefined +>type : "rectangle" | "circle" | undefined + + case 'circle': +>'circle' : "circle" + + return Math.PI * shape.radius ** 2 +>Math.PI * shape.radius ** 2 : number +>Math.PI : number +>Math : Math +>PI : number +>shape.radius ** 2 : number +>shape.radius : number +>shape : { type: "circle"; radius: number; } +>radius : number +>2 : 2 + + case 'rectangle': +>'rectangle' : "rectangle" + + return shape.width * shape.height +>shape.width * shape.height : number +>shape.width : number +>shape : { type: "rectangle"; width: number; height: number; } +>width : number +>shape.height : number +>shape : { type: "rectangle"; width: number; height: number; } +>height : number + + default: + return assertUndefined(shape) +>assertUndefined(shape) : void +>assertUndefined : (v: undefined) => void +>shape : never + } +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowOptionalChain1.ts b/tests/cases/conformance/controlFlow/controlFlowOptionalChain1.ts new file mode 100644 index 0000000000000..d3c972ed27314 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowOptionalChain1.ts @@ -0,0 +1,18 @@ +// @strict: true + +type Shape = + | { type: 'rectangle', width: number, height: number } + | { type: 'circle', radius: number } + +declare function assertUndefined(v: undefined): void + +function getArea(shape?: Shape) { + switch (shape?.type) { + case 'circle': + return Math.PI * shape.radius ** 2 + case 'rectangle': + return shape.width * shape.height + default: + return assertUndefined(shape) + } +}