From 1d93b76b3f0fecc726b5f64defbe8cb54400b959 Mon Sep 17 00:00:00 2001 From: Dmitrijs Minajevs Date: Fri, 12 Jul 2019 14:04:19 +0300 Subject: [PATCH 1/8] Added "readonly" to Type Keywords --- src/harness/fourslash.ts | 6 +++--- src/services/utilities.ts | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index ffc0759e92e..c8c1643b0e0 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -865,7 +865,7 @@ namespace FourSlash { ts.zipWith(actual, expected, (completion, expectedCompletion, index) => { const name = typeof expectedCompletion === "string" ? expectedCompletion : expectedCompletion.name; if (completion.name !== name) { - this.raiseError(`${marker ? JSON.stringify(marker) : "" } Expected completion at index ${index} to be ${name}, got ${completion.name}`); + this.raiseError(`${marker ? JSON.stringify(marker) : ""} Expected completion at index ${index} to be ${name}, got ${completion.name}`); } this.verifyCompletionEntry(completion, expectedCompletion); }); @@ -3742,7 +3742,7 @@ namespace FourSlashInterface { } export class Plugins { - constructor (private state: FourSlash.TestState) { + constructor(private state: FourSlash.TestState) { } public configurePlugin(pluginName: string, configuration: any): void { @@ -4565,7 +4565,7 @@ namespace FourSlashInterface { export const keywords: ReadonlyArray = keywordsWithUndefined.filter(k => k.name !== "undefined"); export const typeKeywords: ReadonlyArray = - ["false", "null", "true", "void", "any", "boolean", "keyof", "never", "number", "object", "string", "symbol", "undefined", "unique", "unknown", "bigint"].map(keywordEntry); + ["false", "null", "true", "void", "any", "boolean", "keyof", "never", "readonly", "number", "object", "string", "symbol", "undefined", "unique", "unknown", "bigint"].map(keywordEntry); const globalTypeDecls: ReadonlyArray = [ interfaceEntry("Symbol"), diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 44125fea6d0..7a273b64ad1 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1224,6 +1224,7 @@ namespace ts { SyntaxKind.NullKeyword, SyntaxKind.NumberKeyword, SyntaxKind.ObjectKeyword, + SyntaxKind.ReadonlyKeyword, SyntaxKind.StringKeyword, SyntaxKind.SymbolKeyword, SyntaxKind.TrueKeyword, @@ -1751,8 +1752,8 @@ namespace ts { function getSynthesizedDeepCloneWorker(node: T, renameMap?: Map, checker?: TypeChecker, callback?: (originalNode: Node, clone: Node) => any): T { const visited = (renameMap || checker || callback) ? - visitEachChild(node, wrapper, nullTransformationContext) : - visitEachChild(node, getSynthesizedDeepClone, nullTransformationContext); + visitEachChild(node, wrapper, nullTransformationContext) : + visitEachChild(node, getSynthesizedDeepClone, nullTransformationContext); if (visited === node) { // This only happens for leaf nodes - internal nodes always see their children change. From 74805c2e2392707af8aa606f8f7a52dfc28bdabb Mon Sep 17 00:00:00 2001 From: Dmitrijs Minajevs Date: Fri, 12 Jul 2019 14:11:23 +0300 Subject: [PATCH 2/8] Fixed failing test due to changed details --- .../fourslash/documentHighlightsInvalidModifierLocations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cases/fourslash/documentHighlightsInvalidModifierLocations.ts b/tests/cases/fourslash/documentHighlightsInvalidModifierLocations.ts index f008e632464..baa203901fd 100644 --- a/tests/cases/fourslash/documentHighlightsInvalidModifierLocations.ts +++ b/tests/cases/fourslash/documentHighlightsInvalidModifierLocations.ts @@ -6,5 +6,5 @@ ////function f([|readonly|] p) {} for (const r of test.ranges()) { - verify.documentHighlightsOf(r, [r]); + verify.documentHighlightsOf(r, test.ranges()); } From b2c555a57dfc2f2ad3c029d97ebc6f0932a2614e Mon Sep 17 00:00:00 2001 From: Dmitrijs Minajevs Date: Fri, 12 Jul 2019 15:25:00 +0300 Subject: [PATCH 3/8] Added new keword compeltion filter for assertions --- src/services/completions.ts | 27 +++++++++++++------ .../cases/fourslash/completionListAsConst.ts | 11 ++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 tests/cases/fourslash/completionListAsConst.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 6d1d48b22b8..f95526121a3 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -38,6 +38,7 @@ namespace ts.Completions { InterfaceElementKeywords, // Keywords inside interface body ConstructorParameterKeywords, // Keywords at constructor parameter FunctionLikeBodyKeywords, // Keywords at function like body + TypeAssertionKeywords, TypeKeywords, Last = TypeKeywords } @@ -441,7 +442,7 @@ namespace ts.Completions { (symbol.escapedName === InternalSymbolName.ExportEquals)) // Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase. ? firstDefined(symbol.declarations, d => isExportAssignment(d) && isIdentifier(d.expression) ? d.expression.text : undefined) - || codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target) + || codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target) : symbol.name; } @@ -632,9 +633,9 @@ namespace ts.Completions { // At `,`, treat this as the next argument after the comma. ? checker.getContextualTypeForArgumentAtIndex(argInfo.invocation, argInfo.argumentIndex + (previousToken.kind === SyntaxKind.CommaToken ? 1 : 0)) : isEqualityOperatorKind(previousToken.kind) && isBinaryExpression(parent) && isEqualityOperatorKind(parent.operatorToken.kind) - // completion at `x ===/**/` should be for the right side - ? checker.getTypeAtLocation(parent.left) - : checker.getContextualType(previousToken as Expression); + // completion at `x ===/**/` should be for the right side + ? checker.getTypeAtLocation(parent.left) + : checker.getContextualType(previousToken as Expression); } } @@ -1181,7 +1182,11 @@ namespace ts.Completions { function filterGlobalCompletion(symbols: Symbol[]): void { const isTypeOnly = isTypeOnlyCompletion(); const allowTypes = isTypeOnly || !isContextTokenValueLocation(contextToken) && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker); - if (isTypeOnly) keywordFilters = KeywordCompletionFilters.TypeKeywords; + if (isTypeOnly) { + keywordFilters = isInsideTypeAssertion() + ? KeywordCompletionFilters.TypeAssertionKeywords + : KeywordCompletionFilters.TypeKeywords; + } filterMutate(symbols, symbol => { if (!isSourceFile(location)) { @@ -1211,6 +1216,10 @@ namespace ts.Completions { }); } + function isInsideTypeAssertion(): boolean { + return isAsExpression(contextToken.parent); + } + function isTypeOnlyCompletion(): boolean { return insideJsDocTagTypeExpression || !isContextTokenValueLocation(contextToken) && (isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken)); } @@ -1434,7 +1443,7 @@ namespace ts.Completions { // 3. at the end of a regular expression (due to trailing flags like '/foo/g'). return (isRegularExpressionLiteral(contextToken) || isStringTextContainingNode(contextToken)) && ( rangeContainsPositionExclusive(createTextRangeFromSpan(createTextSpanFromNode(contextToken)), position) || - position === contextToken.end && (!!contextToken.isUnterminated || isRegularExpressionLiteral(contextToken))); + position === contextToken.end && (!!contextToken.isUnterminated || isRegularExpressionLiteral(contextToken))); } /** @@ -1944,8 +1953,8 @@ namespace ts.Completions { return baseSymbols.filter(propertySymbol => !existingMemberNames.has(propertySymbol.escapedName) && - !!propertySymbol.declarations && - !(getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.Private)); + !!propertySymbol.declarations && + !(getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.Private)); } /** @@ -2057,6 +2066,8 @@ namespace ts.Completions { return isParameterPropertyModifier(kind); case KeywordCompletionFilters.FunctionLikeBodyKeywords: return isFunctionLikeBodyKeyword(kind); + case KeywordCompletionFilters.TypeAssertionKeywords: + return isTypeKeyword(kind) || kind === SyntaxKind.ConstKeyword; case KeywordCompletionFilters.TypeKeywords: return isTypeKeyword(kind); default: diff --git a/tests/cases/fourslash/completionListAsConst.ts b/tests/cases/fourslash/completionListAsConst.ts new file mode 100644 index 00000000000..3a8b706cea2 --- /dev/null +++ b/tests/cases/fourslash/completionListAsConst.ts @@ -0,0 +1,11 @@ +/// + +////const a = { +//// b: 42 as con/*0*/ +////}; +//// +////1 as con/*1*/ +//// +////const b = 42 as /*2*/ + +verify.completions({ marker: test.markers(), includes: [{ name: "const", sortText: completion.SortText.GlobalsOrKeywords }] }); From 9a37ef86676263a37f6068d02af96461aa00238a Mon Sep 17 00:00:00 2001 From: Dmitrijs Minajevs Date: Tue, 16 Jul 2019 10:04:14 +0300 Subject: [PATCH 4/8] typeAssertionKeywords tests --- src/harness/fourslash.ts | 3 +++ tests/cases/fourslash/completionListAsConst.ts | 11 ----------- .../fourslash/completionsTypeAssertionKeywords.ts | 13 +++++++++++++ tests/cases/fourslash/fourslash.ts | 1 + 4 files changed, 17 insertions(+), 11 deletions(-) delete mode 100644 tests/cases/fourslash/completionListAsConst.ts create mode 100644 tests/cases/fourslash/completionsTypeAssertionKeywords.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index c8c1643b0e0..28e2659418f 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -4681,6 +4681,9 @@ namespace FourSlashInterface { ]; } + export const typeAssertionKeywords: ReadonlyArray = + globalTypesPlus([keywordEntry("const")]); + function getInJsKeywords(keywords: ReadonlyArray): ReadonlyArray { return keywords.filter(keyword => { switch (keyword.name) { diff --git a/tests/cases/fourslash/completionListAsConst.ts b/tests/cases/fourslash/completionListAsConst.ts deleted file mode 100644 index 3a8b706cea2..00000000000 --- a/tests/cases/fourslash/completionListAsConst.ts +++ /dev/null @@ -1,11 +0,0 @@ -/// - -////const a = { -//// b: 42 as con/*0*/ -////}; -//// -////1 as con/*1*/ -//// -////const b = 42 as /*2*/ - -verify.completions({ marker: test.markers(), includes: [{ name: "const", sortText: completion.SortText.GlobalsOrKeywords }] }); diff --git a/tests/cases/fourslash/completionsTypeAssertionKeywords.ts b/tests/cases/fourslash/completionsTypeAssertionKeywords.ts new file mode 100644 index 00000000000..100ab6e51e2 --- /dev/null +++ b/tests/cases/fourslash/completionsTypeAssertionKeywords.ts @@ -0,0 +1,13 @@ +/// + +////const a = { +//// b: 42 as /*0*/ +////}; +//// +////1 as /*1*/ +//// +////const b = 42 as /*2*/ +//// +////var c = 42 + +verify.completions({ marker: test.markers(), exact: completion.typeAssertionKeywords }); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 05799a15642..47c835d9c88 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -696,6 +696,7 @@ declare namespace completion { export const typeKeywords: ReadonlyArray; export const globalTypes: ReadonlyArray; export function globalTypesPlus(plus: ReadonlyArray): ReadonlyArray; + export const typeAssertionKeywords: ReadonlyArray; export const classElementKeywords: ReadonlyArray; export const classElementInJsKeywords: ReadonlyArray; export const constructorParameterKeywords: ReadonlyArray; From 0075b0a6a55ac6f27a05bdbfbcce4614f40761d1 Mon Sep 17 00:00:00 2001 From: Dmitrijs Minajevs Date: Tue, 16 Jul 2019 10:06:16 +0300 Subject: [PATCH 5/8] Fix for angle-bracket type assertion --- src/services/completions.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index f95526121a3..cd70f0ddcdd 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1183,7 +1183,7 @@ namespace ts.Completions { const isTypeOnly = isTypeOnlyCompletion(); const allowTypes = isTypeOnly || !isContextTokenValueLocation(contextToken) && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker); if (isTypeOnly) { - keywordFilters = isInsideTypeAssertion() + keywordFilters = isTypeAssertion() ? KeywordCompletionFilters.TypeAssertionKeywords : KeywordCompletionFilters.TypeKeywords; } @@ -1216,8 +1216,8 @@ namespace ts.Completions { }); } - function isInsideTypeAssertion(): boolean { - return isAsExpression(contextToken.parent); + function isTypeAssertion(): boolean { + return isAssertionExpression(contextToken.parent); } function isTypeOnlyCompletion(): boolean { @@ -1249,6 +1249,9 @@ namespace ts.Completions { case SyntaxKind.ExtendsKeyword: return parentKind === SyntaxKind.TypeParameter; + + case SyntaxKind.LessThanToken: + return parentKind === SyntaxKind.TypeAssertionExpression; } } return false; From 84cdc63d1faf89885cd2b24de57e2c29d8901651 Mon Sep 17 00:00:00 2001 From: Dmitrijs Minajevs Date: Tue, 16 Jul 2019 11:00:45 +0300 Subject: [PATCH 6/8] Merge angle-bracket fix --- src/services/completions.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index dc23c0a9b12..d7b40d14587 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1249,13 +1249,11 @@ namespace ts.Completions { return parentKind === SyntaxKind.AsExpression; case SyntaxKind.LessThanToken: - return parentKind === SyntaxKind.TypeReference; + return parentKind === SyntaxKind.TypeReference || + parentKind === SyntaxKind.TypeAssertionExpression; case SyntaxKind.ExtendsKeyword: return parentKind === SyntaxKind.TypeParameter; - - case SyntaxKind.LessThanToken: - return parentKind === SyntaxKind.TypeAssertionExpression; } } return false; From 1de76cd605c8abcf61ffadebe3e515f1b56eab1c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 16 Jul 2019 10:10:58 -0700 Subject: [PATCH 7/8] Control flow for element access expressions (#31478) * Control flow for element access expressions Draft version, just want to see how performance is * Add baselines * Fix cast lint * Cleanup to share code path * Fix errant diffs --- src/compiler/checker.ts | 13 ++-- .../reference/controlFlowElementAccess2.js | 25 ++++++++ .../controlFlowElementAccess2.symbols | 37 +++++++++++ .../reference/controlFlowElementAccess2.types | 63 +++++++++++++++++++ .../controlFlow/controlFlowElementAccess2.ts | 13 ++++ 5 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/controlFlowElementAccess2.js create mode 100644 tests/baselines/reference/controlFlowElementAccess2.symbols create mode 100644 tests/baselines/reference/controlFlowElementAccess2.types create mode 100644 tests/cases/conformance/controlFlow/controlFlowElementAccess2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c31732cc24a..06395d935b7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20573,10 +20573,15 @@ namespace ts { } propType = getConstraintForLocation(getTypeOfSymbol(prop), node); } + return getFlowTypeOfAccessExpression(node, prop, propType, right); + } + + function getFlowTypeOfAccessExpression(node: ElementAccessExpression | PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, propType: Type, errorNode: Node) { // Only compute control flow type if this is a property access expression that isn't an // assignment target, and the referenced property was declared as a variable, property, // accessor, or optional method. - if (node.kind !== SyntaxKind.PropertyAccessExpression || + const assignmentKind = getAssignmentTargetKind(node); + if (node.kind !== SyntaxKind.ElementAccessExpression && node.kind !== SyntaxKind.PropertyAccessExpression || assignmentKind === AssignmentKind.Definite || prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) { return propType; @@ -20586,7 +20591,7 @@ namespace ts { // and if we are in a constructor of the same class as the property declaration, assume that // the property is uninitialized at the top of the control flow. let assumeUninitialized = false; - if (strictNullChecks && strictPropertyInitialization && left.kind === SyntaxKind.ThisKeyword) { + if (strictNullChecks && strictPropertyInitialization && node.expression.kind === SyntaxKind.ThisKeyword) { const declaration = prop && prop.valueDeclaration; if (declaration && isInstancePropertyWithoutInitializer(declaration)) { const flowContainer = getControlFlowContainer(node); @@ -20603,7 +20608,7 @@ namespace ts { } 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 + error(errorNode, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop!)); // TODO: GH#18217 // Return the declared type to reduce follow-on errors return propType; } @@ -20960,7 +20965,7 @@ namespace ts { AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) : AccessFlags.None; const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, node, accessFlags) || errorType; - return checkIndexedAccessIndexType(indexedAccessType, node); + return checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, indexedAccessType.symbol, indexedAccessType, indexExpression), node); } function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean { diff --git a/tests/baselines/reference/controlFlowElementAccess2.js b/tests/baselines/reference/controlFlowElementAccess2.js new file mode 100644 index 00000000000..050fc15b8af --- /dev/null +++ b/tests/baselines/reference/controlFlowElementAccess2.js @@ -0,0 +1,25 @@ +//// [controlFlowElementAccess2.ts] +declare const config: { + [key: string]: boolean | { prop: string }; +}; + +if (typeof config['works'] !== 'boolean') { + config.works.prop = 'test'; // ok + config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string } +} +if (typeof config.works !== 'boolean') { + config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string } + config.works.prop = 'test'; // ok +} + + +//// [controlFlowElementAccess2.js] +"use strict"; +if (typeof config['works'] !== 'boolean') { + config.works.prop = 'test'; // ok + config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string } +} +if (typeof config.works !== 'boolean') { + config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string } + config.works.prop = 'test'; // ok +} diff --git a/tests/baselines/reference/controlFlowElementAccess2.symbols b/tests/baselines/reference/controlFlowElementAccess2.symbols new file mode 100644 index 00000000000..5cdd890a759 --- /dev/null +++ b/tests/baselines/reference/controlFlowElementAccess2.symbols @@ -0,0 +1,37 @@ +=== tests/cases/conformance/controlFlow/controlFlowElementAccess2.ts === +declare const config: { +>config : Symbol(config, Decl(controlFlowElementAccess2.ts, 0, 13)) + + [key: string]: boolean | { prop: string }; +>key : Symbol(key, Decl(controlFlowElementAccess2.ts, 1, 5)) +>prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30)) + +}; + +if (typeof config['works'] !== 'boolean') { +>config : Symbol(config, Decl(controlFlowElementAccess2.ts, 0, 13)) + + config.works.prop = 'test'; // ok +>config.works.prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30)) +>config : Symbol(config, Decl(controlFlowElementAccess2.ts, 0, 13)) +>prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30)) + + config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string } +>config['works'].prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30)) +>config : Symbol(config, Decl(controlFlowElementAccess2.ts, 0, 13)) +>prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30)) +} +if (typeof config.works !== 'boolean') { +>config : Symbol(config, Decl(controlFlowElementAccess2.ts, 0, 13)) + + config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string } +>config['works'].prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30)) +>config : Symbol(config, Decl(controlFlowElementAccess2.ts, 0, 13)) +>prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30)) + + config.works.prop = 'test'; // ok +>config.works.prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30)) +>config : Symbol(config, Decl(controlFlowElementAccess2.ts, 0, 13)) +>prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30)) +} + diff --git a/tests/baselines/reference/controlFlowElementAccess2.types b/tests/baselines/reference/controlFlowElementAccess2.types new file mode 100644 index 00000000000..65dca4de502 --- /dev/null +++ b/tests/baselines/reference/controlFlowElementAccess2.types @@ -0,0 +1,63 @@ +=== tests/cases/conformance/controlFlow/controlFlowElementAccess2.ts === +declare const config: { +>config : { [key: string]: boolean | { prop: string; }; } + + [key: string]: boolean | { prop: string }; +>key : string +>prop : string + +}; + +if (typeof config['works'] !== 'boolean') { +>typeof config['works'] !== 'boolean' : boolean +>typeof config['works'] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>config['works'] : boolean | { prop: string; } +>config : { [key: string]: boolean | { prop: string; }; } +>'works' : "works" +>'boolean' : "boolean" + + config.works.prop = 'test'; // ok +>config.works.prop = 'test' : "test" +>config.works.prop : string +>config.works : { prop: string; } +>config : { [key: string]: boolean | { prop: string; }; } +>works : { prop: string; } +>prop : string +>'test' : "test" + + config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string } +>config['works'].prop = 'test' : "test" +>config['works'].prop : string +>config['works'] : { prop: string; } +>config : { [key: string]: boolean | { prop: string; }; } +>'works' : "works" +>prop : string +>'test' : "test" +} +if (typeof config.works !== 'boolean') { +>typeof config.works !== 'boolean' : boolean +>typeof config.works : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>config.works : boolean | { prop: string; } +>config : { [key: string]: boolean | { prop: string; }; } +>works : boolean | { prop: string; } +>'boolean' : "boolean" + + config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string } +>config['works'].prop = 'test' : "test" +>config['works'].prop : string +>config['works'] : { prop: string; } +>config : { [key: string]: boolean | { prop: string; }; } +>'works' : "works" +>prop : string +>'test' : "test" + + config.works.prop = 'test'; // ok +>config.works.prop = 'test' : "test" +>config.works.prop : string +>config.works : { prop: string; } +>config : { [key: string]: boolean | { prop: string; }; } +>works : { prop: string; } +>prop : string +>'test' : "test" +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowElementAccess2.ts b/tests/cases/conformance/controlFlow/controlFlowElementAccess2.ts new file mode 100644 index 00000000000..fa0592c973e --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowElementAccess2.ts @@ -0,0 +1,13 @@ +// @strict: true +declare const config: { + [key: string]: boolean | { prop: string }; +}; + +if (typeof config['works'] !== 'boolean') { + config.works.prop = 'test'; // ok + config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string } +} +if (typeof config.works !== 'boolean') { + config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string } + config.works.prop = 'test'; // ok +} From dc38aceb028765ff5e19a3fe920f3cbf09e14064 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 16 Jul 2019 11:38:09 -0700 Subject: [PATCH 8/8] Fix the export on TestServerHostCreationParameters to fix build break after LKG Its not detected currently is because LKG doesnt have #32156 --- src/harness/virtualFileSystemWithWatch.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index f49f15cadc2..b8e3d189781 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -30,7 +30,7 @@ interface Array {}` return combinePaths(getDirectoryPath(libFile.path), "tsc.js"); } - interface TestServerHostCreationParameters { + export interface TestServerHostCreationParameters { useCaseSensitiveFileNames?: boolean; executingFilePath?: string; currentDirectory?: string;