diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 11f279d27d5..2eae269b1a3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14734,6 +14734,14 @@ namespace ts { // reference 'x.y.z', we may be at an assignment to 'x.y' or 'x'. In that case, // return the declared type. if (containsMatchingReference(reference, node)) { + // A matching dotted name might also be an expando property on a function *expression*, + // in which case we continue control flow analysis back to the function's declaration + if (isVariableDeclaration(node) && (isInJSFile(node) || isVarConst(node))) { + const init = getDeclaredExpandoInitializer(node); + if (init && (init.kind === SyntaxKind.FunctionExpression || init.kind === SyntaxKind.ArrowFunction)) { + return getTypeAtFlowNode(flow.antecedent); + } + } return declaredType; } // Assignment doesn't affect reference @@ -18395,6 +18403,9 @@ namespace ts { } } } + else if (strictNullChecks && prop && prop.valueDeclaration && isPropertyAccessExpression(prop.valueDeclaration) && getAssignmentDeclarationPropertyAccessKind(prop.valueDeclaration)) { + assumeUninitialized = true; + } const flowType = getFlowTypeOfReference(node, propType, assumeUninitialized ? getOptionalType(propType) : propType); if (assumeUninitialized && !(getFalsyFlags(propType) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) { error(right, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop!)); // TODO: GH#18217 diff --git a/tests/baselines/reference/typeFromPropertyAssignment36.errors.txt b/tests/baselines/reference/typeFromPropertyAssignment36.errors.txt new file mode 100644 index 00000000000..3cd98f56b4b --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment36.errors.txt @@ -0,0 +1,85 @@ +tests/cases/conformance/salsa/typeFromPropertyAssignment36.ts(11,7): error TS2565: Property 'q' is used before being assigned. +tests/cases/conformance/salsa/typeFromPropertyAssignment36.ts(42,3): error TS2565: Property 'q' is used before being assigned. +tests/cases/conformance/salsa/typeFromPropertyAssignment36.ts(64,3): error TS2565: Property 'expando' is used before being assigned. + + +==== tests/cases/conformance/salsa/typeFromPropertyAssignment36.ts (3 errors) ==== + function f(b: boolean) { + function d() { + } + d.e = 12 + d.e + + if (b) { + d.q = false + } + // error d.q might not be assigned + d.q + ~ +!!! error TS2565: Property 'q' is used before being assigned. + if (b) { + d.q = false + } + else { + d.q = true + } + d.q + if (b) { + d.r = 1 + } + else { + d.r = 2 + } + d.r + if (b) { + d.s = 'hi' + } + return d + } + // OK to access possibly-unassigned properties outside the initialising scope + var test = f(true).s + + function d() { + } + d.e = 12 + d.e + + if (!!false) { + d.q = false + } + d.q + ~ +!!! error TS2565: Property 'q' is used before being assigned. + if (!!false) { + d.q = false + } + else { + d.q = true + } + d.q + if (!!false) { + d.r = 1 + } + else { + d.r = 2 + } + d.r + + // test function expressions too + const g = function() { + } + if (!!false) { + g.expando = 1 + } + g.expando // error + ~~~~~~~ +!!! error TS2565: Property 'expando' is used before being assigned. + + if (!!false) { + g.both = 'hi' + } + else { + g.both = 0 + } + g.both + \ No newline at end of file diff --git a/tests/baselines/reference/typeFromPropertyAssignment36.js b/tests/baselines/reference/typeFromPropertyAssignment36.js new file mode 100644 index 00000000000..ae0c2ec244b --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment36.js @@ -0,0 +1,144 @@ +//// [typeFromPropertyAssignment36.ts] +function f(b: boolean) { + function d() { + } + d.e = 12 + d.e + + if (b) { + d.q = false + } + // error d.q might not be assigned + d.q + if (b) { + d.q = false + } + else { + d.q = true + } + d.q + if (b) { + d.r = 1 + } + else { + d.r = 2 + } + d.r + if (b) { + d.s = 'hi' + } + return d +} +// OK to access possibly-unassigned properties outside the initialising scope +var test = f(true).s + +function d() { +} +d.e = 12 +d.e + +if (!!false) { + d.q = false +} +d.q +if (!!false) { + d.q = false +} +else { + d.q = true +} +d.q +if (!!false) { + d.r = 1 +} +else { + d.r = 2 +} +d.r + +// test function expressions too +const g = function() { +} +if (!!false) { + g.expando = 1 +} +g.expando // error + +if (!!false) { + g.both = 'hi' +} +else { + g.both = 0 +} +g.both + + +//// [typeFromPropertyAssignment36.js] +"use strict"; +function f(b) { + function d() { + } + d.e = 12; + d.e; + if (b) { + d.q = false; + } + // error d.q might not be assigned + d.q; + if (b) { + d.q = false; + } + else { + d.q = true; + } + d.q; + if (b) { + d.r = 1; + } + else { + d.r = 2; + } + d.r; + if (b) { + d.s = 'hi'; + } + return d; +} +// OK to access possibly-unassigned properties outside the initialising scope +var test = f(true).s; +function d() { +} +d.e = 12; +d.e; +if (!!false) { + d.q = false; +} +d.q; +if (!!false) { + d.q = false; +} +else { + d.q = true; +} +d.q; +if (!!false) { + d.r = 1; +} +else { + d.r = 2; +} +d.r; +// test function expressions too +var g = function () { +}; +if (!!false) { + g.expando = 1; +} +g.expando; // error +if (!!false) { + g.both = 'hi'; +} +else { + g.both = 0; +} +g.both; diff --git a/tests/baselines/reference/typeFromPropertyAssignment36.symbols b/tests/baselines/reference/typeFromPropertyAssignment36.symbols new file mode 100644 index 00000000000..1c90cf5684c --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment36.symbols @@ -0,0 +1,178 @@ +=== tests/cases/conformance/salsa/typeFromPropertyAssignment36.ts === +function f(b: boolean) { +>f : Symbol(f, Decl(typeFromPropertyAssignment36.ts, 0, 0)) +>b : Symbol(b, Decl(typeFromPropertyAssignment36.ts, 0, 11)) + + function d() { +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) + } + d.e = 12 +>d.e : Symbol(d.e, Decl(typeFromPropertyAssignment36.ts, 2, 5)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) +>e : Symbol(d.e, Decl(typeFromPropertyAssignment36.ts, 2, 5)) + + d.e +>d.e : Symbol(d.e, Decl(typeFromPropertyAssignment36.ts, 2, 5)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) +>e : Symbol(d.e, Decl(typeFromPropertyAssignment36.ts, 2, 5)) + + if (b) { +>b : Symbol(b, Decl(typeFromPropertyAssignment36.ts, 0, 11)) + + d.q = false +>d.q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 6, 12), Decl(typeFromPropertyAssignment36.ts, 11, 12), Decl(typeFromPropertyAssignment36.ts, 14, 10)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) +>q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 6, 12), Decl(typeFromPropertyAssignment36.ts, 11, 12), Decl(typeFromPropertyAssignment36.ts, 14, 10)) + } + // error d.q might not be assigned + d.q +>d.q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 6, 12), Decl(typeFromPropertyAssignment36.ts, 11, 12), Decl(typeFromPropertyAssignment36.ts, 14, 10)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) +>q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 6, 12), Decl(typeFromPropertyAssignment36.ts, 11, 12), Decl(typeFromPropertyAssignment36.ts, 14, 10)) + + if (b) { +>b : Symbol(b, Decl(typeFromPropertyAssignment36.ts, 0, 11)) + + d.q = false +>d.q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 6, 12), Decl(typeFromPropertyAssignment36.ts, 11, 12), Decl(typeFromPropertyAssignment36.ts, 14, 10)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) +>q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 6, 12), Decl(typeFromPropertyAssignment36.ts, 11, 12), Decl(typeFromPropertyAssignment36.ts, 14, 10)) + } + else { + d.q = true +>d.q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 6, 12), Decl(typeFromPropertyAssignment36.ts, 11, 12), Decl(typeFromPropertyAssignment36.ts, 14, 10)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) +>q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 6, 12), Decl(typeFromPropertyAssignment36.ts, 11, 12), Decl(typeFromPropertyAssignment36.ts, 14, 10)) + } + d.q +>d.q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 6, 12), Decl(typeFromPropertyAssignment36.ts, 11, 12), Decl(typeFromPropertyAssignment36.ts, 14, 10)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) +>q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 6, 12), Decl(typeFromPropertyAssignment36.ts, 11, 12), Decl(typeFromPropertyAssignment36.ts, 14, 10)) + + if (b) { +>b : Symbol(b, Decl(typeFromPropertyAssignment36.ts, 0, 11)) + + d.r = 1 +>d.r : Symbol(d.r, Decl(typeFromPropertyAssignment36.ts, 18, 12), Decl(typeFromPropertyAssignment36.ts, 21, 10)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) +>r : Symbol(d.r, Decl(typeFromPropertyAssignment36.ts, 18, 12), Decl(typeFromPropertyAssignment36.ts, 21, 10)) + } + else { + d.r = 2 +>d.r : Symbol(d.r, Decl(typeFromPropertyAssignment36.ts, 18, 12), Decl(typeFromPropertyAssignment36.ts, 21, 10)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) +>r : Symbol(d.r, Decl(typeFromPropertyAssignment36.ts, 18, 12), Decl(typeFromPropertyAssignment36.ts, 21, 10)) + } + d.r +>d.r : Symbol(d.r, Decl(typeFromPropertyAssignment36.ts, 18, 12), Decl(typeFromPropertyAssignment36.ts, 21, 10)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) +>r : Symbol(d.r, Decl(typeFromPropertyAssignment36.ts, 18, 12), Decl(typeFromPropertyAssignment36.ts, 21, 10)) + + if (b) { +>b : Symbol(b, Decl(typeFromPropertyAssignment36.ts, 0, 11)) + + d.s = 'hi' +>d.s : Symbol(d.s, Decl(typeFromPropertyAssignment36.ts, 25, 12)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) +>s : Symbol(d.s, Decl(typeFromPropertyAssignment36.ts, 25, 12)) + } + return d +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 0, 24)) +} +// OK to access possibly-unassigned properties outside the initialising scope +var test = f(true).s +>test : Symbol(test, Decl(typeFromPropertyAssignment36.ts, 31, 3)) +>f(true).s : Symbol(d.s, Decl(typeFromPropertyAssignment36.ts, 25, 12)) +>f : Symbol(f, Decl(typeFromPropertyAssignment36.ts, 0, 0)) +>s : Symbol(d.s, Decl(typeFromPropertyAssignment36.ts, 25, 12)) + +function d() { +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 31, 20), Decl(typeFromPropertyAssignment36.ts, 34, 1)) +} +d.e = 12 +>d.e : Symbol(d.e, Decl(typeFromPropertyAssignment36.ts, 34, 1)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 31, 20), Decl(typeFromPropertyAssignment36.ts, 34, 1)) +>e : Symbol(d.e, Decl(typeFromPropertyAssignment36.ts, 34, 1)) + +d.e +>d.e : Symbol(d.e, Decl(typeFromPropertyAssignment36.ts, 34, 1)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 31, 20), Decl(typeFromPropertyAssignment36.ts, 34, 1)) +>e : Symbol(d.e, Decl(typeFromPropertyAssignment36.ts, 34, 1)) + +if (!!false) { + d.q = false +>d.q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 38, 14), Decl(typeFromPropertyAssignment36.ts, 42, 14), Decl(typeFromPropertyAssignment36.ts, 45, 6)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 31, 20), Decl(typeFromPropertyAssignment36.ts, 34, 1)) +>q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 38, 14), Decl(typeFromPropertyAssignment36.ts, 42, 14), Decl(typeFromPropertyAssignment36.ts, 45, 6)) +} +d.q +>d.q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 38, 14), Decl(typeFromPropertyAssignment36.ts, 42, 14), Decl(typeFromPropertyAssignment36.ts, 45, 6)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 31, 20), Decl(typeFromPropertyAssignment36.ts, 34, 1)) +>q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 38, 14), Decl(typeFromPropertyAssignment36.ts, 42, 14), Decl(typeFromPropertyAssignment36.ts, 45, 6)) + +if (!!false) { + d.q = false +>d.q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 38, 14), Decl(typeFromPropertyAssignment36.ts, 42, 14), Decl(typeFromPropertyAssignment36.ts, 45, 6)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 31, 20), Decl(typeFromPropertyAssignment36.ts, 34, 1)) +>q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 38, 14), Decl(typeFromPropertyAssignment36.ts, 42, 14), Decl(typeFromPropertyAssignment36.ts, 45, 6)) +} +else { + d.q = true +>d.q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 38, 14), Decl(typeFromPropertyAssignment36.ts, 42, 14), Decl(typeFromPropertyAssignment36.ts, 45, 6)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 31, 20), Decl(typeFromPropertyAssignment36.ts, 34, 1)) +>q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 38, 14), Decl(typeFromPropertyAssignment36.ts, 42, 14), Decl(typeFromPropertyAssignment36.ts, 45, 6)) +} +d.q +>d.q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 38, 14), Decl(typeFromPropertyAssignment36.ts, 42, 14), Decl(typeFromPropertyAssignment36.ts, 45, 6)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 31, 20), Decl(typeFromPropertyAssignment36.ts, 34, 1)) +>q : Symbol(d.q, Decl(typeFromPropertyAssignment36.ts, 38, 14), Decl(typeFromPropertyAssignment36.ts, 42, 14), Decl(typeFromPropertyAssignment36.ts, 45, 6)) + +if (!!false) { + d.r = 1 +>d.r : Symbol(d.r, Decl(typeFromPropertyAssignment36.ts, 49, 14), Decl(typeFromPropertyAssignment36.ts, 52, 6)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 31, 20), Decl(typeFromPropertyAssignment36.ts, 34, 1)) +>r : Symbol(d.r, Decl(typeFromPropertyAssignment36.ts, 49, 14), Decl(typeFromPropertyAssignment36.ts, 52, 6)) +} +else { + d.r = 2 +>d.r : Symbol(d.r, Decl(typeFromPropertyAssignment36.ts, 49, 14), Decl(typeFromPropertyAssignment36.ts, 52, 6)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 31, 20), Decl(typeFromPropertyAssignment36.ts, 34, 1)) +>r : Symbol(d.r, Decl(typeFromPropertyAssignment36.ts, 49, 14), Decl(typeFromPropertyAssignment36.ts, 52, 6)) +} +d.r +>d.r : Symbol(d.r, Decl(typeFromPropertyAssignment36.ts, 49, 14), Decl(typeFromPropertyAssignment36.ts, 52, 6)) +>d : Symbol(d, Decl(typeFromPropertyAssignment36.ts, 31, 20), Decl(typeFromPropertyAssignment36.ts, 34, 1)) +>r : Symbol(d.r, Decl(typeFromPropertyAssignment36.ts, 49, 14), Decl(typeFromPropertyAssignment36.ts, 52, 6)) + +// test function expressions too +const g = function() { +>g : Symbol(g, Decl(typeFromPropertyAssignment36.ts, 58, 5)) +} +if (!!false) { + g.expando = 1 +>g.expando : Symbol(g.expando, Decl(typeFromPropertyAssignment36.ts, 60, 14)) +>g : Symbol(g, Decl(typeFromPropertyAssignment36.ts, 58, 5)) +>expando : Symbol(g.expando, Decl(typeFromPropertyAssignment36.ts, 60, 14)) +} +g.expando // error +>g.expando : Symbol(g.expando, Decl(typeFromPropertyAssignment36.ts, 60, 14)) +>g : Symbol(g, Decl(typeFromPropertyAssignment36.ts, 58, 5)) +>expando : Symbol(g.expando, Decl(typeFromPropertyAssignment36.ts, 60, 14)) + +if (!!false) { + g.both = 'hi' +>g.both : Symbol(g.both, Decl(typeFromPropertyAssignment36.ts, 65, 14), Decl(typeFromPropertyAssignment36.ts, 68, 6)) +>g : Symbol(g, Decl(typeFromPropertyAssignment36.ts, 58, 5)) +>both : Symbol(g.both, Decl(typeFromPropertyAssignment36.ts, 65, 14), Decl(typeFromPropertyAssignment36.ts, 68, 6)) +} +else { + g.both = 0 +>g.both : Symbol(g.both, Decl(typeFromPropertyAssignment36.ts, 65, 14), Decl(typeFromPropertyAssignment36.ts, 68, 6)) +>g : Symbol(g, Decl(typeFromPropertyAssignment36.ts, 58, 5)) +>both : Symbol(g.both, Decl(typeFromPropertyAssignment36.ts, 65, 14), Decl(typeFromPropertyAssignment36.ts, 68, 6)) +} +g.both +>g.both : Symbol(g.both, Decl(typeFromPropertyAssignment36.ts, 65, 14), Decl(typeFromPropertyAssignment36.ts, 68, 6)) +>g : Symbol(g, Decl(typeFromPropertyAssignment36.ts, 58, 5)) +>both : Symbol(g.both, Decl(typeFromPropertyAssignment36.ts, 65, 14), Decl(typeFromPropertyAssignment36.ts, 68, 6)) + diff --git a/tests/baselines/reference/typeFromPropertyAssignment36.types b/tests/baselines/reference/typeFromPropertyAssignment36.types new file mode 100644 index 00000000000..4eb184fa0bb --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment36.types @@ -0,0 +1,233 @@ +=== tests/cases/conformance/salsa/typeFromPropertyAssignment36.ts === +function f(b: boolean) { +>f : (b: boolean) => { (): void; e: number; q: boolean; r: number; s: string; } +>b : boolean + + function d() { +>d : { (): void; e: number; q: boolean; r: number; s: string; } + } + d.e = 12 +>d.e = 12 : 12 +>d.e : number +>d : { (): void; e: number; q: boolean; r: number; s: string; } +>e : number +>12 : 12 + + d.e +>d.e : number +>d : { (): void; e: number; q: boolean; r: number; s: string; } +>e : number + + if (b) { +>b : boolean + + d.q = false +>d.q = false : false +>d.q : boolean +>d : { (): void; e: number; q: boolean; r: number; s: string; } +>q : boolean +>false : false + } + // error d.q might not be assigned + d.q +>d.q : boolean +>d : { (): void; e: number; q: boolean; r: number; s: string; } +>q : boolean + + if (b) { +>b : boolean + + d.q = false +>d.q = false : false +>d.q : boolean +>d : { (): void; e: number; q: boolean; r: number; s: string; } +>q : boolean +>false : false + } + else { + d.q = true +>d.q = true : true +>d.q : boolean +>d : { (): void; e: number; q: boolean; r: number; s: string; } +>q : boolean +>true : true + } + d.q +>d.q : boolean +>d : { (): void; e: number; q: boolean; r: number; s: string; } +>q : boolean + + if (b) { +>b : boolean + + d.r = 1 +>d.r = 1 : 1 +>d.r : number +>d : { (): void; e: number; q: boolean; r: number; s: string; } +>r : number +>1 : 1 + } + else { + d.r = 2 +>d.r = 2 : 2 +>d.r : number +>d : { (): void; e: number; q: boolean; r: number; s: string; } +>r : number +>2 : 2 + } + d.r +>d.r : number +>d : { (): void; e: number; q: boolean; r: number; s: string; } +>r : number + + if (b) { +>b : boolean + + d.s = 'hi' +>d.s = 'hi' : "hi" +>d.s : string +>d : { (): void; e: number; q: boolean; r: number; s: string; } +>s : string +>'hi' : "hi" + } + return d +>d : { (): void; e: number; q: boolean; r: number; s: string; } +} +// OK to access possibly-unassigned properties outside the initialising scope +var test = f(true).s +>test : string +>f(true).s : string +>f(true) : { (): void; e: number; q: boolean; r: number; s: string; } +>f : (b: boolean) => { (): void; e: number; q: boolean; r: number; s: string; } +>true : true +>s : string + +function d() { +>d : typeof d +} +d.e = 12 +>d.e = 12 : 12 +>d.e : number +>d : typeof d +>e : number +>12 : 12 + +d.e +>d.e : number +>d : typeof d +>e : number + +if (!!false) { +>!!false : false +>!false : true +>false : false + + d.q = false +>d.q = false : false +>d.q : boolean +>d : typeof d +>q : boolean +>false : false +} +d.q +>d.q : boolean +>d : typeof d +>q : boolean + +if (!!false) { +>!!false : false +>!false : true +>false : false + + d.q = false +>d.q = false : false +>d.q : boolean +>d : typeof d +>q : boolean +>false : false +} +else { + d.q = true +>d.q = true : true +>d.q : boolean +>d : typeof d +>q : boolean +>true : true +} +d.q +>d.q : boolean +>d : typeof d +>q : boolean + +if (!!false) { +>!!false : false +>!false : true +>false : false + + d.r = 1 +>d.r = 1 : 1 +>d.r : number +>d : typeof d +>r : number +>1 : 1 +} +else { + d.r = 2 +>d.r = 2 : 2 +>d.r : number +>d : typeof d +>r : number +>2 : 2 +} +d.r +>d.r : number +>d : typeof d +>r : number + +// test function expressions too +const g = function() { +>g : { (): void; expando: number; both: string | number; } +>function() {} : { (): void; expando: number; both: string | number; } +} +if (!!false) { +>!!false : false +>!false : true +>false : false + + g.expando = 1 +>g.expando = 1 : 1 +>g.expando : number +>g : { (): void; expando: number; both: string | number; } +>expando : number +>1 : 1 +} +g.expando // error +>g.expando : number +>g : { (): void; expando: number; both: string | number; } +>expando : number + +if (!!false) { +>!!false : false +>!false : true +>false : false + + g.both = 'hi' +>g.both = 'hi' : "hi" +>g.both : string | number +>g : { (): void; expando: number; both: string | number; } +>both : string | number +>'hi' : "hi" +} +else { + g.both = 0 +>g.both = 0 : 0 +>g.both : string | number +>g : { (): void; expando: number; both: string | number; } +>both : string | number +>0 : 0 +} +g.both +>g.both : string | number +>g : { (): void; expando: number; both: string | number; } +>both : string | number + diff --git a/tests/cases/conformance/salsa/typeFromPropertyAssignment36.ts b/tests/cases/conformance/salsa/typeFromPropertyAssignment36.ts new file mode 100644 index 00000000000..ee1038f48ca --- /dev/null +++ b/tests/cases/conformance/salsa/typeFromPropertyAssignment36.ts @@ -0,0 +1,73 @@ +// @strict: true +function f(b: boolean) { + function d() { + } + d.e = 12 + d.e + + if (b) { + d.q = false + } + // error d.q might not be assigned + d.q + if (b) { + d.q = false + } + else { + d.q = true + } + d.q + if (b) { + d.r = 1 + } + else { + d.r = 2 + } + d.r + if (b) { + d.s = 'hi' + } + return d +} +// OK to access possibly-unassigned properties outside the initialising scope +var test = f(true).s + +function d() { +} +d.e = 12 +d.e + +if (!!false) { + d.q = false +} +d.q +if (!!false) { + d.q = false +} +else { + d.q = true +} +d.q +if (!!false) { + d.r = 1 +} +else { + d.r = 2 +} +d.r + +// test function expressions too +const g = function() { +} +if (!!false) { + g.expando = 1 +} +g.expando // error + +if (!!false) { + g.both = 'hi' +} +else { + g.both = 0 +} +g.both