diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1d8ca25170303..14432d7689442 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16,6 +16,7 @@ namespace ts { YieldStarFlag = 1 << 4, SpreadFlag = 1 << 5, DestructuringFlag = 1 << 6, + PossiblyOutOfBounds = 1 << 7, // Spread, Destructuring, Array element assignment Element = AllowsSyncIterablesFlag, @@ -185,6 +186,7 @@ namespace ts { Writing = 1 << 1, CacheSymbol = 1 << 2, NoTupleBoundsCheck = 1 << 3, + ExpressionPosition = 1 << 4, } const enum SignatureCheckMode { @@ -7828,7 +7830,7 @@ namespace ts { // Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form) const name = declaration.propertyName || declaration.name; const indexType = getLiteralTypeFromPropertyName(name); - const declaredType = getConstraintForLocation(getIndexedAccessType(parentType, indexType, name), declaration.name); + const declaredType = getConstraintForLocation(getIndexedAccessType(parentType, indexType, name, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, AccessFlags.ExpressionPosition), declaration.name); type = getFlowTypeOfDestructuring(declaration, declaredType); } } @@ -7836,7 +7838,7 @@ namespace ts { // This elementType will be used if the specific property corresponding to this index is not // present (aka the tuple element property). This call also checks that the parentType is in // fact an iterable or array (depending on target language). - const elementType = checkIteratedTypeOrElementType(IterationUse.Destructuring, parentType, undefinedType, pattern); + const elementType = checkIteratedTypeOrElementType(IterationUse.Destructuring | (declaration.dotDotDotToken ? 0 : IterationUse.PossiblyOutOfBounds), parentType, undefinedType, pattern); const index = pattern.elements.indexOf(declaration); if (declaration.dotDotDotToken) { // If the parent is a tuple type, the rest element has a tuple type of the @@ -7849,7 +7851,7 @@ namespace ts { else if (isArrayLikeType(parentType)) { const indexType = getLiteralType(index); const accessFlags = hasDefaultValue(declaration) ? AccessFlags.NoTupleBoundsCheck : 0; - const declaredType = getConstraintForLocation(getIndexedAccessTypeOrUndefined(parentType, indexType, declaration.name, accessFlags) || errorType, declaration.name); + const declaredType = getConstraintForLocation(getIndexedAccessTypeOrUndefined(parentType, indexType, declaration.name, accessFlags | AccessFlags.ExpressionPosition) || errorType, declaration.name); type = getFlowTypeOfDestructuring(declaration, declaredType); } else { @@ -13639,13 +13641,16 @@ namespace ts { } return undefined; } + const shouldIncludeUndefined = + compilerOptions.noUncheckedIndexSignatures && + (accessFlags & (AccessFlags.Writing | AccessFlags.ExpressionPosition)) === AccessFlags.ExpressionPosition; if (accessNode && !isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) { const indexNode = getIndexNodeForAccessExpression(accessNode); error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(indexType)); - return indexInfo.type; + return shouldIncludeUndefined ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type; } errorIfWritingToReadonlyIndex(indexInfo); - return indexInfo.type; + return shouldIncludeUndefined ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type; } if (indexType.flags & TypeFlags.Never) { return neverType; @@ -13887,8 +13892,8 @@ namespace ts { return instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper); } - function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { - return getIndexedAccessTypeOrUndefined(objectType, indexType, accessNode, AccessFlags.None, aliasSymbol, aliasTypeArguments) || (accessNode ? errorType : unknownType); + function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], accessFlags = AccessFlags.None): Type { + return getIndexedAccessTypeOrUndefined(objectType, indexType, accessNode, accessFlags, aliasSymbol, aliasTypeArguments) || (accessNode ? errorType : unknownType); } function indexTypeLessThan(indexType: Type, limit: number) { @@ -13908,6 +13913,11 @@ namespace ts { if (objectType === wildcardType || indexType === wildcardType) { return wildcardType; } + + const shouldIncludeUndefined = + compilerOptions.noUncheckedIndexSignatures && + (accessFlags & (AccessFlags.Writing | AccessFlags.ExpressionPosition)) === AccessFlags.ExpressionPosition; + // If the object type has a string index signature and no other members we know that the result will // always be the type of that index signature and we can simplify accordingly. if (isStringIndexSignatureOnlyType(objectType) && !(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) { @@ -13931,7 +13941,8 @@ namespace ts { if (!type) { indexedAccessTypes.set(id, type = createIndexedAccessType(objectType, indexType, aliasSymbol, aliasTypeArguments)); } - return type; + + return shouldIncludeUndefined ? getUnionType([type, undefinedType]) : type; } // In the following we resolve T[K] to the type of the property in T selected by K. // We treat boolean as different from other unions to improve errors; @@ -13957,7 +13968,9 @@ namespace ts { if (wasMissingProp) { return undefined; } - return accessFlags & AccessFlags.Writing ? getIntersectionType(propTypes, aliasSymbol, aliasTypeArguments) : getUnionType(propTypes, UnionReduction.Literal, aliasSymbol, aliasTypeArguments); + return accessFlags & AccessFlags.Writing + ? getIntersectionType(propTypes, aliasSymbol, aliasTypeArguments) + : getUnionType(propTypes, UnionReduction.Literal, aliasSymbol, aliasTypeArguments); } return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, /* supressNoImplicitAnyError */ false, accessNode, accessFlags | AccessFlags.CacheSymbol, /* reportDeprecated */ true); } @@ -20704,17 +20717,24 @@ namespace ts { if (!isTypeUsableAsPropertyName(nameType)) return errorType; const text = getPropertyNameFromType(nameType); return getConstraintForLocation(getTypeOfPropertyOfType(type, text), name) || - isNumericLiteralName(text) && getIndexTypeOfType(type, IndexKind.Number) || - getIndexTypeOfType(type, IndexKind.String) || + isNumericLiteralName(text) && includeUndefinedInIndexSignature(getIndexTypeOfType(type, IndexKind.Number)) || + includeUndefinedInIndexSignature(getIndexTypeOfType(type, IndexKind.String)) || errorType; } function getTypeOfDestructuredArrayElement(type: Type, index: number) { return everyType(type, isTupleLikeType) && getTupleElementType(type, index) || - checkIteratedTypeOrElementType(IterationUse.Destructuring, type, undefinedType, /*errorNode*/ undefined) || + includeUndefinedInIndexSignature(checkIteratedTypeOrElementType(IterationUse.Destructuring, type, undefinedType, /*errorNode*/ undefined)) || errorType; } + function includeUndefinedInIndexSignature(type: Type | undefined): Type | undefined { + if (!type) return type; + return compilerOptions.noUncheckedIndexSignatures ? + getUnionType([type, undefinedType]) : + type; + } + function getTypeOfDestructuredSpreadExpression(type: Type) { return createArrayType(checkIteratedTypeOrElementType(IterationUse.Destructuring, type, undefinedType, /*errorNode*/ undefined) || errorType); } @@ -25452,7 +25472,8 @@ namespace ts { if (indexInfo.isReadonly && (isAssignmentTarget(node) || isDeleteTarget(node))) { error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType)); } - propType = indexInfo.type; + + propType = (compilerOptions.noUncheckedIndexSignatures && !isAssignmentTarget(node)) ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type; } else { if (prop.valueDeclaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(node, prop)) { @@ -25892,7 +25913,7 @@ namespace ts { const accessFlags = isAssignmentTarget(node) ? AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) : AccessFlags.None; - const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, node, accessFlags) || errorType; + const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, node, accessFlags | AccessFlags.ExpressionPosition) || errorType; return checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, indexedAccessType.symbol, indexedAccessType, indexExpression), node); } @@ -29358,7 +29379,7 @@ namespace ts { checkPropertyAccessibility(property, /*isSuper*/ false, objectLiteralType, prop); } } - const elementType = getIndexedAccessType(objectLiteralType, exprType, name); + const elementType = getIndexedAccessType(objectLiteralType, exprType, name, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, AccessFlags.ExpressionPosition); const type = getFlowTypeOfDestructuring(property, elementType); return checkDestructuringAssignment(property.kind === SyntaxKind.ShorthandPropertyAssignment ? property : property.initializer, type); } @@ -29396,9 +29417,14 @@ namespace ts { // This elementType will be used if the specific property corresponding to this index is not // present (aka the tuple element property). This call also checks that the parentType is in // fact an iterable or array (depending on target language). - const elementType = checkIteratedTypeOrElementType(IterationUse.Destructuring, sourceType, undefinedType, node) || errorType; + const possiblyOutOfBoundsType = checkIteratedTypeOrElementType(IterationUse.Destructuring | IterationUse.PossiblyOutOfBounds, sourceType, undefinedType, node) || errorType; + let inBoundsType: Type | undefined = compilerOptions.noUncheckedIndexSignatures ? undefined: possiblyOutOfBoundsType; for (let i = 0; i < elements.length; i++) { - checkArrayLiteralDestructuringElementAssignment(node, sourceType, i, elementType, checkMode); + let type = possiblyOutOfBoundsType; + if (node.elements[i].kind === SyntaxKind.SpreadElement) { + type = inBoundsType = inBoundsType ?? (checkIteratedTypeOrElementType(IterationUse.Destructuring, sourceType, undefinedType, node) || errorType); + } + checkArrayLiteralDestructuringElementAssignment(node, sourceType, i, type, checkMode); } return sourceType; } @@ -29413,7 +29439,7 @@ namespace ts { if (isArrayLikeType(sourceType)) { // We create a synthetic expression so that getIndexedAccessType doesn't get confused // when the element is a SyntaxKind.ElementAccessExpression. - const accessFlags = hasDefaultValue(element) ? AccessFlags.NoTupleBoundsCheck : 0; + const accessFlags = AccessFlags.ExpressionPosition | (hasDefaultValue(element) ? AccessFlags.NoTupleBoundsCheck : 0); const elementType = getIndexedAccessTypeOrUndefined(sourceType, indexType, createSyntheticExpression(element, indexType), accessFlags) || errorType; const assignedType = hasDefaultValue(element) ? getTypeWithFacts(elementType, TypeFacts.NEUndefined) : elementType; const type = getFlowTypeOfDestructuring(element, assignedType); @@ -33656,6 +33682,7 @@ namespace ts { const uplevelIteration = languageVersion >= ScriptTarget.ES2015; const downlevelIteration = !uplevelIteration && compilerOptions.downlevelIteration; + const possibleOutOfBounds = compilerOptions.noUncheckedIndexSignatures && !!(use & IterationUse.PossiblyOutOfBounds); // Get the iterated type of an `Iterable` or `IterableIterator` only in ES2015 // or higher, when inside of an async generator or for-await-if, or when @@ -33677,7 +33704,7 @@ namespace ts { } } if (iterationTypes || uplevelIteration) { - return iterationTypes && iterationTypes.yieldType; + return possibleOutOfBounds ? includeUndefinedInIndexSignature(iterationTypes && iterationTypes.yieldType) : (iterationTypes && iterationTypes.yieldType); } } @@ -33714,7 +33741,7 @@ namespace ts { // Now that we've removed all the StringLike types, if no constituents remain, then the entire // arrayOrStringType was a string. if (arrayType.flags & TypeFlags.Never) { - return stringType; + return possibleOutOfBounds ? includeUndefinedInIndexSignature(stringType) : stringType; } } } @@ -33744,20 +33771,20 @@ namespace ts { defaultDiagnostic, typeToString(arrayType)); } - return hasStringConstituent ? stringType : undefined; + return hasStringConstituent ? possibleOutOfBounds ? includeUndefinedInIndexSignature(stringType) : stringType : undefined; } const arrayElementType = getIndexTypeOfType(arrayType, IndexKind.Number); if (hasStringConstituent && arrayElementType) { // This is just an optimization for the case where arrayOrStringType is string | string[] - if (arrayElementType.flags & TypeFlags.StringLike) { + if (arrayElementType.flags & TypeFlags.StringLike && !compilerOptions.noUncheckedIndexSignatures) { return stringType; } - return getUnionType([arrayElementType, stringType], UnionReduction.Subtype); + return getUnionType(possibleOutOfBounds ? [arrayElementType, stringType, undefinedType] : [arrayElementType, stringType], UnionReduction.Subtype); } - return arrayElementType; + return (use & IterationUse.PossiblyOutOfBounds) ? includeUndefinedInIndexSignature(arrayElementType) : arrayElementType; } /** diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 64706e39a9838..beb601b0b8f6f 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -624,6 +624,14 @@ namespace ts { category: Diagnostics.Additional_Checks, description: Diagnostics.Report_errors_for_fallthrough_cases_in_switch_statement }, + { + name: "noUncheckedIndexedAccess", + type: "boolean", + affectsSemanticDiagnostics: true, + showInSimplifiedHelpView: false, + category: Diagnostics.Additional_Checks, + description: Diagnostics.Include_undefined_in_index_signature_results + }, // Module Resolution { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 55dfd36380195..fe9beaa1121e6 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4721,6 +4721,11 @@ "code": 6504 }, + "Include 'undefined' in index signature results": { + "category": "Message", + "code": 6800 + }, + "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", "code": 7005 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9590bded376ed..71d64d3c65bac 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5783,6 +5783,7 @@ namespace ts { assumeChangesOnlyAffectDirectDependencies?: boolean; noLib?: boolean; noResolve?: boolean; + noUncheckedIndexSignatures?: boolean; out?: string; outDir?: string; outFile?: string; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 67e6c6384e50c..2e5b82492bac6 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2841,6 +2841,7 @@ declare namespace ts { assumeChangesOnlyAffectDirectDependencies?: boolean; noLib?: boolean; noResolve?: boolean; + noUncheckedIndexSignatures?: boolean; out?: string; outDir?: string; outFile?: string; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 1da3e0a94122f..5810c60b4672b 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2841,6 +2841,7 @@ declare namespace ts { assumeChangesOnlyAffectDirectDependencies?: boolean; noLib?: boolean; noResolve?: boolean; + noUncheckedIndexSignatures?: boolean; out?: string; outDir?: string; outFile?: string; diff --git a/tests/baselines/reference/noUncheckedIndexedAccess.errors.txt b/tests/baselines/reference/noUncheckedIndexedAccess.errors.txt new file mode 100644 index 0000000000000..9177245e367f3 --- /dev/null +++ b/tests/baselines/reference/noUncheckedIndexedAccess.errors.txt @@ -0,0 +1,109 @@ +tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts(3,32): error TS2344: Type 'boolean | undefined' does not satisfy the constraint 'boolean'. + Type 'undefined' is not assignable to type 'boolean'. +tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts(38,1): error TS2322: Type 'undefined' is not assignable to type 'boolean'. +tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts(39,1): error TS2322: Type 'undefined' is not assignable to type 'boolean'. +tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts(40,1): error TS2322: Type 'undefined' is not assignable to type 'boolean'. +tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts(41,1): error TS2322: Type 'undefined' is not assignable to type 'boolean'. +tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts(85,1): error TS2322: Type 'undefined' is not assignable to type 'string'. + + +==== tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts (6 errors) ==== + type CheckBooleanOnly = any; + // Validate CheckBooleanOnly works - should error + type T_ERR1 = CheckBooleanOnly; + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2344: Type 'boolean | undefined' does not satisfy the constraint 'boolean'. +!!! error TS2344: Type 'undefined' is not assignable to type 'boolean'. + + enum NumericEnum1 { A, B, C } + enum NumericEnum2 { A = 0, B = 1 , C = 2 } + enum StringEnum1 { A = "Alpha", B = "Beta" } + + declare const strMap: { [s: string]: boolean }; + + // All of these should be errors + const e1: boolean = strMap["foo"]; + const e2: boolean = strMap.bar; + const e3: boolean = strMap[0]; + const e4: boolean = strMap[0 as string | number]; + const e5: boolean = strMap[0 as string | 0 | 1]; + const e6: boolean = strMap[0 as 0 | 1]; + const e7: boolean = strMap["foo" as "foo" | "baz"]; + const e8: boolean = strMap[NumericEnum1.A]; + const e9: boolean = strMap[NumericEnum2.A]; + const e10: boolean = strMap[StringEnum1.A]; + const e11: boolean = strMap[StringEnum1.A as StringEnum1]; + const e12: boolean = strMap[NumericEnum1.A as NumericEnum1]; + const e13: boolean = strMap[NumericEnum2.A as NumericEnum2]; + const e14: boolean = strMap[null as any]; + + // Should be OK + const ok1: boolean | undefined = strMap["foo"]; + const ok2: boolean | undefined = strMap.bar; + + type T_OK1 = CheckBooleanOnly<(typeof strMap)[string]>; + type T_OK2 = CheckBooleanOnly<(typeof strMap)["foo"]>; + type T_OK3 = CheckBooleanOnly<(typeof strMap)["bar" | "baz"]>; + type T_OK4 = CheckBooleanOnly<(typeof strMap)[number]>; + type T_OK5 = CheckBooleanOnly<(typeof strMap)[any]>; + + // Writes don't allow 'undefined'; all should be errors + strMap["baz"] = undefined; + ~~~~~~~~~~~~~ +!!! error TS2322: Type 'undefined' is not assignable to type 'boolean'. + strMap.qua = undefined; + ~~~~~~~~~~ +!!! error TS2322: Type 'undefined' is not assignable to type 'boolean'. + strMap[0] = undefined; + ~~~~~~~~~ +!!! error TS2322: Type 'undefined' is not assignable to type 'boolean'. + strMap[null as any] = undefined; + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type 'undefined' is not assignable to type 'boolean'. + + // Numeric lookups are unaffected + declare const numMap: { [s: number]: boolean }; + // All of these should be ok + const num_ok1: boolean = numMap[0]; + const num_ok2: boolean = numMap[0 as number]; + const num_ok3: boolean = numMap[0 as 0 | 1]; + const num_ok4: boolean = numMap[NumericEnum1.A]; + const num_ok5: boolean = numMap[NumericEnum2.A]; + + // Generics + function generic1(arg: T): boolean { + // Should error + return arg["blah"]; + } + function generic2(arg: T): boolean { + // Should OK + return arg["blah"]!; + } + function generic3(arg: T): boolean { + // Should error + return strMap[arg]; + } + + // Element access into known properties is ok + declare const obj1: { x: string, y: number, [key: string]: string | number }; + obj1["x"]; + const y = "y"; + obj1[y]; + let yy = "y"; + obj1[yy]; + let z = "z"; + obj1[z]; + + // Distributivity cases + declare const strMapUnion: { [s: string]: boolean } | { [s: string]: number }; + // Should error + const f1: boolean | number = strMapUnion["foo"]; + + // Symbol index signatures + declare const s: unique symbol; + declare const symbolMap: { [s]: string }; + const e15: string = symbolMap[s]; // Should OK + symbolMap[s] = undefined; // Should error + ~~~~~~~~~~~~ +!!! error TS2322: Type 'undefined' is not assignable to type 'string'. + \ No newline at end of file diff --git a/tests/baselines/reference/noUncheckedIndexedAccess.js b/tests/baselines/reference/noUncheckedIndexedAccess.js new file mode 100644 index 0000000000000..c03f4f76bb706 --- /dev/null +++ b/tests/baselines/reference/noUncheckedIndexedAccess.js @@ -0,0 +1,160 @@ +//// [noUncheckedIndexedAccess.ts] +type CheckBooleanOnly = any; +// Validate CheckBooleanOnly works - should error +type T_ERR1 = CheckBooleanOnly; + +enum NumericEnum1 { A, B, C } +enum NumericEnum2 { A = 0, B = 1 , C = 2 } +enum StringEnum1 { A = "Alpha", B = "Beta" } + +declare const strMap: { [s: string]: boolean }; + +// All of these should be errors +const e1: boolean = strMap["foo"]; +const e2: boolean = strMap.bar; +const e3: boolean = strMap[0]; +const e4: boolean = strMap[0 as string | number]; +const e5: boolean = strMap[0 as string | 0 | 1]; +const e6: boolean = strMap[0 as 0 | 1]; +const e7: boolean = strMap["foo" as "foo" | "baz"]; +const e8: boolean = strMap[NumericEnum1.A]; +const e9: boolean = strMap[NumericEnum2.A]; +const e10: boolean = strMap[StringEnum1.A]; +const e11: boolean = strMap[StringEnum1.A as StringEnum1]; +const e12: boolean = strMap[NumericEnum1.A as NumericEnum1]; +const e13: boolean = strMap[NumericEnum2.A as NumericEnum2]; +const e14: boolean = strMap[null as any]; + +// Should be OK +const ok1: boolean | undefined = strMap["foo"]; +const ok2: boolean | undefined = strMap.bar; + +type T_OK1 = CheckBooleanOnly<(typeof strMap)[string]>; +type T_OK2 = CheckBooleanOnly<(typeof strMap)["foo"]>; +type T_OK3 = CheckBooleanOnly<(typeof strMap)["bar" | "baz"]>; +type T_OK4 = CheckBooleanOnly<(typeof strMap)[number]>; +type T_OK5 = CheckBooleanOnly<(typeof strMap)[any]>; + +// Writes don't allow 'undefined'; all should be errors +strMap["baz"] = undefined; +strMap.qua = undefined; +strMap[0] = undefined; +strMap[null as any] = undefined; + +// Numeric lookups are unaffected +declare const numMap: { [s: number]: boolean }; +// All of these should be ok +const num_ok1: boolean = numMap[0]; +const num_ok2: boolean = numMap[0 as number]; +const num_ok3: boolean = numMap[0 as 0 | 1]; +const num_ok4: boolean = numMap[NumericEnum1.A]; +const num_ok5: boolean = numMap[NumericEnum2.A]; + +// Generics +function generic1(arg: T): boolean { + // Should error + return arg["blah"]; +} +function generic2(arg: T): boolean { + // Should OK + return arg["blah"]!; +} +function generic3(arg: T): boolean { + // Should error + return strMap[arg]; +} + +// Element access into known properties is ok +declare const obj1: { x: string, y: number, [key: string]: string | number }; +obj1["x"]; +const y = "y"; +obj1[y]; +let yy = "y"; +obj1[yy]; +let z = "z"; +obj1[z]; + +// Distributivity cases +declare const strMapUnion: { [s: string]: boolean } | { [s: string]: number }; +// Should error +const f1: boolean | number = strMapUnion["foo"]; + +// Symbol index signatures +declare const s: unique symbol; +declare const symbolMap: { [s]: string }; +const e15: string = symbolMap[s]; // Should OK +symbolMap[s] = undefined; // Should error + + +//// [noUncheckedIndexedAccess.js] +"use strict"; +var NumericEnum1; +(function (NumericEnum1) { + NumericEnum1[NumericEnum1["A"] = 0] = "A"; + NumericEnum1[NumericEnum1["B"] = 1] = "B"; + NumericEnum1[NumericEnum1["C"] = 2] = "C"; +})(NumericEnum1 || (NumericEnum1 = {})); +var NumericEnum2; +(function (NumericEnum2) { + NumericEnum2[NumericEnum2["A"] = 0] = "A"; + NumericEnum2[NumericEnum2["B"] = 1] = "B"; + NumericEnum2[NumericEnum2["C"] = 2] = "C"; +})(NumericEnum2 || (NumericEnum2 = {})); +var StringEnum1; +(function (StringEnum1) { + StringEnum1["A"] = "Alpha"; + StringEnum1["B"] = "Beta"; +})(StringEnum1 || (StringEnum1 = {})); +// All of these should be errors +var e1 = strMap["foo"]; +var e2 = strMap.bar; +var e3 = strMap[0]; +var e4 = strMap[0]; +var e5 = strMap[0]; +var e6 = strMap[0]; +var e7 = strMap["foo"]; +var e8 = strMap[NumericEnum1.A]; +var e9 = strMap[NumericEnum2.A]; +var e10 = strMap[StringEnum1.A]; +var e11 = strMap[StringEnum1.A]; +var e12 = strMap[NumericEnum1.A]; +var e13 = strMap[NumericEnum2.A]; +var e14 = strMap[null]; +// Should be OK +var ok1 = strMap["foo"]; +var ok2 = strMap.bar; +// Writes don't allow 'undefined'; all should be errors +strMap["baz"] = undefined; +strMap.qua = undefined; +strMap[0] = undefined; +strMap[null] = undefined; +// All of these should be ok +var num_ok1 = numMap[0]; +var num_ok2 = numMap[0]; +var num_ok3 = numMap[0]; +var num_ok4 = numMap[NumericEnum1.A]; +var num_ok5 = numMap[NumericEnum2.A]; +// Generics +function generic1(arg) { + // Should error + return arg["blah"]; +} +function generic2(arg) { + // Should OK + return arg["blah"]; +} +function generic3(arg) { + // Should error + return strMap[arg]; +} +obj1["x"]; +var y = "y"; +obj1[y]; +var yy = "y"; +obj1[yy]; +var z = "z"; +obj1[z]; +// Should error +var f1 = strMapUnion["foo"]; +var e15 = symbolMap[s]; // Should OK +symbolMap[s] = undefined; // Should error diff --git a/tests/baselines/reference/noUncheckedIndexedAccess.symbols b/tests/baselines/reference/noUncheckedIndexedAccess.symbols new file mode 100644 index 0000000000000..f8920d597599b --- /dev/null +++ b/tests/baselines/reference/noUncheckedIndexedAccess.symbols @@ -0,0 +1,289 @@ +=== tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts === +type CheckBooleanOnly = any; +>CheckBooleanOnly : Symbol(CheckBooleanOnly, Decl(noUncheckedIndexedAccess.ts, 0, 0)) +>T : Symbol(T, Decl(noUncheckedIndexedAccess.ts, 0, 22)) + +// Validate CheckBooleanOnly works - should error +type T_ERR1 = CheckBooleanOnly; +>T_ERR1 : Symbol(T_ERR1, Decl(noUncheckedIndexedAccess.ts, 0, 47)) +>CheckBooleanOnly : Symbol(CheckBooleanOnly, Decl(noUncheckedIndexedAccess.ts, 0, 0)) + +enum NumericEnum1 { A, B, C } +>NumericEnum1 : Symbol(NumericEnum1, Decl(noUncheckedIndexedAccess.ts, 2, 52)) +>A : Symbol(NumericEnum1.A, Decl(noUncheckedIndexedAccess.ts, 4, 19)) +>B : Symbol(NumericEnum1.B, Decl(noUncheckedIndexedAccess.ts, 4, 22)) +>C : Symbol(NumericEnum1.C, Decl(noUncheckedIndexedAccess.ts, 4, 25)) + +enum NumericEnum2 { A = 0, B = 1 , C = 2 } +>NumericEnum2 : Symbol(NumericEnum2, Decl(noUncheckedIndexedAccess.ts, 4, 29)) +>A : Symbol(NumericEnum2.A, Decl(noUncheckedIndexedAccess.ts, 5, 19)) +>B : Symbol(NumericEnum2.B, Decl(noUncheckedIndexedAccess.ts, 5, 26)) +>C : Symbol(NumericEnum2.C, Decl(noUncheckedIndexedAccess.ts, 5, 34)) + +enum StringEnum1 { A = "Alpha", B = "Beta" } +>StringEnum1 : Symbol(StringEnum1, Decl(noUncheckedIndexedAccess.ts, 5, 42)) +>A : Symbol(StringEnum1.A, Decl(noUncheckedIndexedAccess.ts, 6, 18)) +>B : Symbol(StringEnum1.B, Decl(noUncheckedIndexedAccess.ts, 6, 31)) + +declare const strMap: { [s: string]: boolean }; +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) +>s : Symbol(s, Decl(noUncheckedIndexedAccess.ts, 8, 25)) + +// All of these should be errors +const e1: boolean = strMap["foo"]; +>e1 : Symbol(e1, Decl(noUncheckedIndexedAccess.ts, 11, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +const e2: boolean = strMap.bar; +>e2 : Symbol(e2, Decl(noUncheckedIndexedAccess.ts, 12, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +const e3: boolean = strMap[0]; +>e3 : Symbol(e3, Decl(noUncheckedIndexedAccess.ts, 13, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +const e4: boolean = strMap[0 as string | number]; +>e4 : Symbol(e4, Decl(noUncheckedIndexedAccess.ts, 14, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +const e5: boolean = strMap[0 as string | 0 | 1]; +>e5 : Symbol(e5, Decl(noUncheckedIndexedAccess.ts, 15, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +const e6: boolean = strMap[0 as 0 | 1]; +>e6 : Symbol(e6, Decl(noUncheckedIndexedAccess.ts, 16, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +const e7: boolean = strMap["foo" as "foo" | "baz"]; +>e7 : Symbol(e7, Decl(noUncheckedIndexedAccess.ts, 17, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +const e8: boolean = strMap[NumericEnum1.A]; +>e8 : Symbol(e8, Decl(noUncheckedIndexedAccess.ts, 18, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) +>NumericEnum1.A : Symbol(NumericEnum1.A, Decl(noUncheckedIndexedAccess.ts, 4, 19)) +>NumericEnum1 : Symbol(NumericEnum1, Decl(noUncheckedIndexedAccess.ts, 2, 52)) +>A : Symbol(NumericEnum1.A, Decl(noUncheckedIndexedAccess.ts, 4, 19)) + +const e9: boolean = strMap[NumericEnum2.A]; +>e9 : Symbol(e9, Decl(noUncheckedIndexedAccess.ts, 19, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) +>NumericEnum2.A : Symbol(NumericEnum2.A, Decl(noUncheckedIndexedAccess.ts, 5, 19)) +>NumericEnum2 : Symbol(NumericEnum2, Decl(noUncheckedIndexedAccess.ts, 4, 29)) +>A : Symbol(NumericEnum2.A, Decl(noUncheckedIndexedAccess.ts, 5, 19)) + +const e10: boolean = strMap[StringEnum1.A]; +>e10 : Symbol(e10, Decl(noUncheckedIndexedAccess.ts, 20, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) +>StringEnum1.A : Symbol(StringEnum1.A, Decl(noUncheckedIndexedAccess.ts, 6, 18)) +>StringEnum1 : Symbol(StringEnum1, Decl(noUncheckedIndexedAccess.ts, 5, 42)) +>A : Symbol(StringEnum1.A, Decl(noUncheckedIndexedAccess.ts, 6, 18)) + +const e11: boolean = strMap[StringEnum1.A as StringEnum1]; +>e11 : Symbol(e11, Decl(noUncheckedIndexedAccess.ts, 21, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) +>StringEnum1.A : Symbol(StringEnum1.A, Decl(noUncheckedIndexedAccess.ts, 6, 18)) +>StringEnum1 : Symbol(StringEnum1, Decl(noUncheckedIndexedAccess.ts, 5, 42)) +>A : Symbol(StringEnum1.A, Decl(noUncheckedIndexedAccess.ts, 6, 18)) +>StringEnum1 : Symbol(StringEnum1, Decl(noUncheckedIndexedAccess.ts, 5, 42)) + +const e12: boolean = strMap[NumericEnum1.A as NumericEnum1]; +>e12 : Symbol(e12, Decl(noUncheckedIndexedAccess.ts, 22, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) +>NumericEnum1.A : Symbol(NumericEnum1.A, Decl(noUncheckedIndexedAccess.ts, 4, 19)) +>NumericEnum1 : Symbol(NumericEnum1, Decl(noUncheckedIndexedAccess.ts, 2, 52)) +>A : Symbol(NumericEnum1.A, Decl(noUncheckedIndexedAccess.ts, 4, 19)) +>NumericEnum1 : Symbol(NumericEnum1, Decl(noUncheckedIndexedAccess.ts, 2, 52)) + +const e13: boolean = strMap[NumericEnum2.A as NumericEnum2]; +>e13 : Symbol(e13, Decl(noUncheckedIndexedAccess.ts, 23, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) +>NumericEnum2.A : Symbol(NumericEnum2.A, Decl(noUncheckedIndexedAccess.ts, 5, 19)) +>NumericEnum2 : Symbol(NumericEnum2, Decl(noUncheckedIndexedAccess.ts, 4, 29)) +>A : Symbol(NumericEnum2.A, Decl(noUncheckedIndexedAccess.ts, 5, 19)) +>NumericEnum2 : Symbol(NumericEnum2, Decl(noUncheckedIndexedAccess.ts, 4, 29)) + +const e14: boolean = strMap[null as any]; +>e14 : Symbol(e14, Decl(noUncheckedIndexedAccess.ts, 24, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +// Should be OK +const ok1: boolean | undefined = strMap["foo"]; +>ok1 : Symbol(ok1, Decl(noUncheckedIndexedAccess.ts, 27, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +const ok2: boolean | undefined = strMap.bar; +>ok2 : Symbol(ok2, Decl(noUncheckedIndexedAccess.ts, 28, 5)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +type T_OK1 = CheckBooleanOnly<(typeof strMap)[string]>; +>T_OK1 : Symbol(T_OK1, Decl(noUncheckedIndexedAccess.ts, 28, 44)) +>CheckBooleanOnly : Symbol(CheckBooleanOnly, Decl(noUncheckedIndexedAccess.ts, 0, 0)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +type T_OK2 = CheckBooleanOnly<(typeof strMap)["foo"]>; +>T_OK2 : Symbol(T_OK2, Decl(noUncheckedIndexedAccess.ts, 30, 55)) +>CheckBooleanOnly : Symbol(CheckBooleanOnly, Decl(noUncheckedIndexedAccess.ts, 0, 0)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +type T_OK3 = CheckBooleanOnly<(typeof strMap)["bar" | "baz"]>; +>T_OK3 : Symbol(T_OK3, Decl(noUncheckedIndexedAccess.ts, 31, 54)) +>CheckBooleanOnly : Symbol(CheckBooleanOnly, Decl(noUncheckedIndexedAccess.ts, 0, 0)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +type T_OK4 = CheckBooleanOnly<(typeof strMap)[number]>; +>T_OK4 : Symbol(T_OK4, Decl(noUncheckedIndexedAccess.ts, 32, 62)) +>CheckBooleanOnly : Symbol(CheckBooleanOnly, Decl(noUncheckedIndexedAccess.ts, 0, 0)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +type T_OK5 = CheckBooleanOnly<(typeof strMap)[any]>; +>T_OK5 : Symbol(T_OK5, Decl(noUncheckedIndexedAccess.ts, 33, 55)) +>CheckBooleanOnly : Symbol(CheckBooleanOnly, Decl(noUncheckedIndexedAccess.ts, 0, 0)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) + +// Writes don't allow 'undefined'; all should be errors +strMap["baz"] = undefined; +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) +>undefined : Symbol(undefined) + +strMap.qua = undefined; +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) +>undefined : Symbol(undefined) + +strMap[0] = undefined; +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) +>undefined : Symbol(undefined) + +strMap[null as any] = undefined; +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) +>undefined : Symbol(undefined) + +// Numeric lookups are unaffected +declare const numMap: { [s: number]: boolean }; +>numMap : Symbol(numMap, Decl(noUncheckedIndexedAccess.ts, 43, 13)) +>s : Symbol(s, Decl(noUncheckedIndexedAccess.ts, 43, 25)) + +// All of these should be ok +const num_ok1: boolean = numMap[0]; +>num_ok1 : Symbol(num_ok1, Decl(noUncheckedIndexedAccess.ts, 45, 5)) +>numMap : Symbol(numMap, Decl(noUncheckedIndexedAccess.ts, 43, 13)) + +const num_ok2: boolean = numMap[0 as number]; +>num_ok2 : Symbol(num_ok2, Decl(noUncheckedIndexedAccess.ts, 46, 5)) +>numMap : Symbol(numMap, Decl(noUncheckedIndexedAccess.ts, 43, 13)) + +const num_ok3: boolean = numMap[0 as 0 | 1]; +>num_ok3 : Symbol(num_ok3, Decl(noUncheckedIndexedAccess.ts, 47, 5)) +>numMap : Symbol(numMap, Decl(noUncheckedIndexedAccess.ts, 43, 13)) + +const num_ok4: boolean = numMap[NumericEnum1.A]; +>num_ok4 : Symbol(num_ok4, Decl(noUncheckedIndexedAccess.ts, 48, 5)) +>numMap : Symbol(numMap, Decl(noUncheckedIndexedAccess.ts, 43, 13)) +>NumericEnum1.A : Symbol(NumericEnum1.A, Decl(noUncheckedIndexedAccess.ts, 4, 19)) +>NumericEnum1 : Symbol(NumericEnum1, Decl(noUncheckedIndexedAccess.ts, 2, 52)) +>A : Symbol(NumericEnum1.A, Decl(noUncheckedIndexedAccess.ts, 4, 19)) + +const num_ok5: boolean = numMap[NumericEnum2.A]; +>num_ok5 : Symbol(num_ok5, Decl(noUncheckedIndexedAccess.ts, 49, 5)) +>numMap : Symbol(numMap, Decl(noUncheckedIndexedAccess.ts, 43, 13)) +>NumericEnum2.A : Symbol(NumericEnum2.A, Decl(noUncheckedIndexedAccess.ts, 5, 19)) +>NumericEnum2 : Symbol(NumericEnum2, Decl(noUncheckedIndexedAccess.ts, 4, 29)) +>A : Symbol(NumericEnum2.A, Decl(noUncheckedIndexedAccess.ts, 5, 19)) + +// Generics +function generic1(arg: T): boolean { +>generic1 : Symbol(generic1, Decl(noUncheckedIndexedAccess.ts, 49, 48)) +>T : Symbol(T, Decl(noUncheckedIndexedAccess.ts, 52, 18)) +>s : Symbol(s, Decl(noUncheckedIndexedAccess.ts, 52, 31)) +>arg : Symbol(arg, Decl(noUncheckedIndexedAccess.ts, 52, 54)) +>T : Symbol(T, Decl(noUncheckedIndexedAccess.ts, 52, 18)) + + // Should error + return arg["blah"]; +>arg : Symbol(arg, Decl(noUncheckedIndexedAccess.ts, 52, 54)) +} +function generic2(arg: T): boolean { +>generic2 : Symbol(generic2, Decl(noUncheckedIndexedAccess.ts, 55, 1)) +>T : Symbol(T, Decl(noUncheckedIndexedAccess.ts, 56, 18)) +>s : Symbol(s, Decl(noUncheckedIndexedAccess.ts, 56, 31)) +>arg : Symbol(arg, Decl(noUncheckedIndexedAccess.ts, 56, 54)) +>T : Symbol(T, Decl(noUncheckedIndexedAccess.ts, 56, 18)) + + // Should OK + return arg["blah"]!; +>arg : Symbol(arg, Decl(noUncheckedIndexedAccess.ts, 56, 54)) +} +function generic3(arg: T): boolean { +>generic3 : Symbol(generic3, Decl(noUncheckedIndexedAccess.ts, 59, 1)) +>T : Symbol(T, Decl(noUncheckedIndexedAccess.ts, 60, 18)) +>arg : Symbol(arg, Decl(noUncheckedIndexedAccess.ts, 60, 36)) +>T : Symbol(T, Decl(noUncheckedIndexedAccess.ts, 60, 18)) + + // Should error + return strMap[arg]; +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccess.ts, 8, 13)) +>arg : Symbol(arg, Decl(noUncheckedIndexedAccess.ts, 60, 36)) +} + +// Element access into known properties is ok +declare const obj1: { x: string, y: number, [key: string]: string | number }; +>obj1 : Symbol(obj1, Decl(noUncheckedIndexedAccess.ts, 66, 13)) +>x : Symbol(x, Decl(noUncheckedIndexedAccess.ts, 66, 21)) +>y : Symbol(y, Decl(noUncheckedIndexedAccess.ts, 66, 32)) +>key : Symbol(key, Decl(noUncheckedIndexedAccess.ts, 66, 45)) + +obj1["x"]; +>obj1 : Symbol(obj1, Decl(noUncheckedIndexedAccess.ts, 66, 13)) +>"x" : Symbol(x, Decl(noUncheckedIndexedAccess.ts, 66, 21)) + +const y = "y"; +>y : Symbol(y, Decl(noUncheckedIndexedAccess.ts, 68, 5)) + +obj1[y]; +>obj1 : Symbol(obj1, Decl(noUncheckedIndexedAccess.ts, 66, 13)) +>y : Symbol(y, Decl(noUncheckedIndexedAccess.ts, 68, 5)) + +let yy = "y"; +>yy : Symbol(yy, Decl(noUncheckedIndexedAccess.ts, 70, 3)) + +obj1[yy]; +>obj1 : Symbol(obj1, Decl(noUncheckedIndexedAccess.ts, 66, 13)) +>yy : Symbol(yy, Decl(noUncheckedIndexedAccess.ts, 70, 3)) + +let z = "z"; +>z : Symbol(z, Decl(noUncheckedIndexedAccess.ts, 72, 3)) + +obj1[z]; +>obj1 : Symbol(obj1, Decl(noUncheckedIndexedAccess.ts, 66, 13)) +>z : Symbol(z, Decl(noUncheckedIndexedAccess.ts, 72, 3)) + +// Distributivity cases +declare const strMapUnion: { [s: string]: boolean } | { [s: string]: number }; +>strMapUnion : Symbol(strMapUnion, Decl(noUncheckedIndexedAccess.ts, 76, 13)) +>s : Symbol(s, Decl(noUncheckedIndexedAccess.ts, 76, 30)) +>s : Symbol(s, Decl(noUncheckedIndexedAccess.ts, 76, 57)) + +// Should error +const f1: boolean | number = strMapUnion["foo"]; +>f1 : Symbol(f1, Decl(noUncheckedIndexedAccess.ts, 78, 5)) +>strMapUnion : Symbol(strMapUnion, Decl(noUncheckedIndexedAccess.ts, 76, 13)) + +// Symbol index signatures +declare const s: unique symbol; +>s : Symbol(s, Decl(noUncheckedIndexedAccess.ts, 81, 13)) + +declare const symbolMap: { [s]: string }; +>symbolMap : Symbol(symbolMap, Decl(noUncheckedIndexedAccess.ts, 82, 13)) +>[s] : Symbol([s], Decl(noUncheckedIndexedAccess.ts, 82, 26)) +>s : Symbol(s, Decl(noUncheckedIndexedAccess.ts, 81, 13)) + +const e15: string = symbolMap[s]; // Should OK +>e15 : Symbol(e15, Decl(noUncheckedIndexedAccess.ts, 83, 5)) +>symbolMap : Symbol(symbolMap, Decl(noUncheckedIndexedAccess.ts, 82, 13)) +>s : Symbol(s, Decl(noUncheckedIndexedAccess.ts, 81, 13)) + +symbolMap[s] = undefined; // Should error +>symbolMap : Symbol(symbolMap, Decl(noUncheckedIndexedAccess.ts, 82, 13)) +>s : Symbol(s, Decl(noUncheckedIndexedAccess.ts, 81, 13)) +>undefined : Symbol(undefined) + diff --git a/tests/baselines/reference/noUncheckedIndexedAccess.types b/tests/baselines/reference/noUncheckedIndexedAccess.types new file mode 100644 index 0000000000000..1aa356cf5d16b --- /dev/null +++ b/tests/baselines/reference/noUncheckedIndexedAccess.types @@ -0,0 +1,353 @@ +=== tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts === +type CheckBooleanOnly = any; +>CheckBooleanOnly : any + +// Validate CheckBooleanOnly works - should error +type T_ERR1 = CheckBooleanOnly; +>T_ERR1 : any + +enum NumericEnum1 { A, B, C } +>NumericEnum1 : NumericEnum1 +>A : NumericEnum1.A +>B : NumericEnum1.B +>C : NumericEnum1.C + +enum NumericEnum2 { A = 0, B = 1 , C = 2 } +>NumericEnum2 : NumericEnum2 +>A : NumericEnum2.A +>0 : 0 +>B : NumericEnum2.B +>1 : 1 +>C : NumericEnum2.C +>2 : 2 + +enum StringEnum1 { A = "Alpha", B = "Beta" } +>StringEnum1 : StringEnum1 +>A : StringEnum1.A +>"Alpha" : "Alpha" +>B : StringEnum1.B +>"Beta" : "Beta" + +declare const strMap: { [s: string]: boolean }; +>strMap : { [s: string]: boolean; } +>s : string + +// All of these should be errors +const e1: boolean = strMap["foo"]; +>e1 : boolean +>strMap["foo"] : boolean +>strMap : { [s: string]: boolean; } +>"foo" : "foo" + +const e2: boolean = strMap.bar; +>e2 : boolean +>strMap.bar : boolean +>strMap : { [s: string]: boolean; } +>bar : boolean + +const e3: boolean = strMap[0]; +>e3 : boolean +>strMap[0] : boolean +>strMap : { [s: string]: boolean; } +>0 : 0 + +const e4: boolean = strMap[0 as string | number]; +>e4 : boolean +>strMap[0 as string | number] : boolean +>strMap : { [s: string]: boolean; } +>0 as string | number : string | number +>0 : 0 + +const e5: boolean = strMap[0 as string | 0 | 1]; +>e5 : boolean +>strMap[0 as string | 0 | 1] : boolean +>strMap : { [s: string]: boolean; } +>0 as string | 0 | 1 : string | 0 | 1 +>0 : 0 + +const e6: boolean = strMap[0 as 0 | 1]; +>e6 : boolean +>strMap[0 as 0 | 1] : boolean +>strMap : { [s: string]: boolean; } +>0 as 0 | 1 : 0 | 1 +>0 : 0 + +const e7: boolean = strMap["foo" as "foo" | "baz"]; +>e7 : boolean +>strMap["foo" as "foo" | "baz"] : boolean +>strMap : { [s: string]: boolean; } +>"foo" as "foo" | "baz" : "foo" | "baz" +>"foo" : "foo" + +const e8: boolean = strMap[NumericEnum1.A]; +>e8 : boolean +>strMap[NumericEnum1.A] : boolean +>strMap : { [s: string]: boolean; } +>NumericEnum1.A : NumericEnum1.A +>NumericEnum1 : typeof NumericEnum1 +>A : NumericEnum1.A + +const e9: boolean = strMap[NumericEnum2.A]; +>e9 : boolean +>strMap[NumericEnum2.A] : boolean +>strMap : { [s: string]: boolean; } +>NumericEnum2.A : NumericEnum2.A +>NumericEnum2 : typeof NumericEnum2 +>A : NumericEnum2.A + +const e10: boolean = strMap[StringEnum1.A]; +>e10 : boolean +>strMap[StringEnum1.A] : boolean +>strMap : { [s: string]: boolean; } +>StringEnum1.A : StringEnum1.A +>StringEnum1 : typeof StringEnum1 +>A : StringEnum1.A + +const e11: boolean = strMap[StringEnum1.A as StringEnum1]; +>e11 : boolean +>strMap[StringEnum1.A as StringEnum1] : boolean +>strMap : { [s: string]: boolean; } +>StringEnum1.A as StringEnum1 : StringEnum1 +>StringEnum1.A : StringEnum1.A +>StringEnum1 : typeof StringEnum1 +>A : StringEnum1.A + +const e12: boolean = strMap[NumericEnum1.A as NumericEnum1]; +>e12 : boolean +>strMap[NumericEnum1.A as NumericEnum1] : boolean +>strMap : { [s: string]: boolean; } +>NumericEnum1.A as NumericEnum1 : NumericEnum1 +>NumericEnum1.A : NumericEnum1.A +>NumericEnum1 : typeof NumericEnum1 +>A : NumericEnum1.A + +const e13: boolean = strMap[NumericEnum2.A as NumericEnum2]; +>e13 : boolean +>strMap[NumericEnum2.A as NumericEnum2] : boolean +>strMap : { [s: string]: boolean; } +>NumericEnum2.A as NumericEnum2 : NumericEnum2 +>NumericEnum2.A : NumericEnum2.A +>NumericEnum2 : typeof NumericEnum2 +>A : NumericEnum2.A + +const e14: boolean = strMap[null as any]; +>e14 : boolean +>strMap[null as any] : boolean +>strMap : { [s: string]: boolean; } +>null as any : any +>null : null + +// Should be OK +const ok1: boolean | undefined = strMap["foo"]; +>ok1 : boolean | undefined +>strMap["foo"] : boolean +>strMap : { [s: string]: boolean; } +>"foo" : "foo" + +const ok2: boolean | undefined = strMap.bar; +>ok2 : boolean | undefined +>strMap.bar : boolean +>strMap : { [s: string]: boolean; } +>bar : boolean + +type T_OK1 = CheckBooleanOnly<(typeof strMap)[string]>; +>T_OK1 : any +>strMap : { [s: string]: boolean; } + +type T_OK2 = CheckBooleanOnly<(typeof strMap)["foo"]>; +>T_OK2 : any +>strMap : { [s: string]: boolean; } + +type T_OK3 = CheckBooleanOnly<(typeof strMap)["bar" | "baz"]>; +>T_OK3 : any +>strMap : { [s: string]: boolean; } + +type T_OK4 = CheckBooleanOnly<(typeof strMap)[number]>; +>T_OK4 : any +>strMap : { [s: string]: boolean; } + +type T_OK5 = CheckBooleanOnly<(typeof strMap)[any]>; +>T_OK5 : any +>strMap : { [s: string]: boolean; } + +// Writes don't allow 'undefined'; all should be errors +strMap["baz"] = undefined; +>strMap["baz"] = undefined : undefined +>strMap["baz"] : boolean +>strMap : { [s: string]: boolean; } +>"baz" : "baz" +>undefined : undefined + +strMap.qua = undefined; +>strMap.qua = undefined : undefined +>strMap.qua : boolean +>strMap : { [s: string]: boolean; } +>qua : boolean +>undefined : undefined + +strMap[0] = undefined; +>strMap[0] = undefined : undefined +>strMap[0] : boolean +>strMap : { [s: string]: boolean; } +>0 : 0 +>undefined : undefined + +strMap[null as any] = undefined; +>strMap[null as any] = undefined : undefined +>strMap[null as any] : boolean +>strMap : { [s: string]: boolean; } +>null as any : any +>null : null +>undefined : undefined + +// Numeric lookups are unaffected +declare const numMap: { [s: number]: boolean }; +>numMap : { [s: number]: boolean; } +>s : number + +// All of these should be ok +const num_ok1: boolean = numMap[0]; +>num_ok1 : boolean +>numMap[0] : boolean +>numMap : { [s: number]: boolean; } +>0 : 0 + +const num_ok2: boolean = numMap[0 as number]; +>num_ok2 : boolean +>numMap[0 as number] : boolean +>numMap : { [s: number]: boolean; } +>0 as number : number +>0 : 0 + +const num_ok3: boolean = numMap[0 as 0 | 1]; +>num_ok3 : boolean +>numMap[0 as 0 | 1] : boolean +>numMap : { [s: number]: boolean; } +>0 as 0 | 1 : 0 | 1 +>0 : 0 + +const num_ok4: boolean = numMap[NumericEnum1.A]; +>num_ok4 : boolean +>numMap[NumericEnum1.A] : boolean +>numMap : { [s: number]: boolean; } +>NumericEnum1.A : NumericEnum1.A +>NumericEnum1 : typeof NumericEnum1 +>A : NumericEnum1.A + +const num_ok5: boolean = numMap[NumericEnum2.A]; +>num_ok5 : boolean +>numMap[NumericEnum2.A] : boolean +>numMap : { [s: number]: boolean; } +>NumericEnum2.A : NumericEnum2.A +>NumericEnum2 : typeof NumericEnum2 +>A : NumericEnum2.A + +// Generics +function generic1(arg: T): boolean { +>generic1 : (arg: T) => boolean +>s : string +>arg : T + + // Should error + return arg["blah"]; +>arg["blah"] : boolean +>arg : T +>"blah" : "blah" +} +function generic2(arg: T): boolean { +>generic2 : (arg: T) => boolean +>s : string +>arg : T + + // Should OK + return arg["blah"]!; +>arg["blah"]! : boolean +>arg["blah"] : boolean +>arg : T +>"blah" : "blah" +} +function generic3(arg: T): boolean { +>generic3 : (arg: T) => boolean +>arg : T + + // Should error + return strMap[arg]; +>strMap[arg] : boolean +>strMap : { [s: string]: boolean; } +>arg : T +} + +// Element access into known properties is ok +declare const obj1: { x: string, y: number, [key: string]: string | number }; +>obj1 : { [key: string]: string | number; x: string; y: number; } +>x : string +>y : number +>key : string + +obj1["x"]; +>obj1["x"] : string +>obj1 : { [key: string]: string | number; x: string; y: number; } +>"x" : "x" + +const y = "y"; +>y : "y" +>"y" : "y" + +obj1[y]; +>obj1[y] : number +>obj1 : { [key: string]: string | number; x: string; y: number; } +>y : "y" + +let yy = "y"; +>yy : string +>"y" : "y" + +obj1[yy]; +>obj1[yy] : string | number +>obj1 : { [key: string]: string | number; x: string; y: number; } +>yy : string + +let z = "z"; +>z : string +>"z" : "z" + +obj1[z]; +>obj1[z] : string | number +>obj1 : { [key: string]: string | number; x: string; y: number; } +>z : string + +// Distributivity cases +declare const strMapUnion: { [s: string]: boolean } | { [s: string]: number }; +>strMapUnion : { [s: string]: boolean; } | { [s: string]: number; } +>s : string +>s : string + +// Should error +const f1: boolean | number = strMapUnion["foo"]; +>f1 : number | boolean +>strMapUnion["foo"] : number | boolean +>strMapUnion : { [s: string]: boolean; } | { [s: string]: number; } +>"foo" : "foo" + +// Symbol index signatures +declare const s: unique symbol; +>s : unique symbol + +declare const symbolMap: { [s]: string }; +>symbolMap : { [s]: string; } +>[s] : string +>s : unique symbol + +const e15: string = symbolMap[s]; // Should OK +>e15 : string +>symbolMap[s] : string +>symbolMap : { [s]: string; } +>s : unique symbol + +symbolMap[s] = undefined; // Should error +>symbolMap[s] = undefined : undefined +>symbolMap[s] : string +>symbolMap : { [s]: string; } +>s : unique symbol +>undefined : undefined + diff --git a/tests/baselines/reference/noUncheckedIndexedAccessDestructuring.errors.txt b/tests/baselines/reference/noUncheckedIndexedAccessDestructuring.errors.txt new file mode 100644 index 0000000000000..dfe57451edcf1 --- /dev/null +++ b/tests/baselines/reference/noUncheckedIndexedAccessDestructuring.errors.txt @@ -0,0 +1,70 @@ +tests/cases/conformance/pedantic/noUncheckedIndexedAccessDestructuring.ts(12,9): error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'string'. +tests/cases/conformance/pedantic/noUncheckedIndexedAccessDestructuring.ts(16,9): error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'string'. + + +==== tests/cases/conformance/pedantic/noUncheckedIndexedAccessDestructuring.ts (2 errors) ==== + declare const strArray: string[]; + declare const strStrTuple: [string, string]; + + // Declaration forms for array destructuring + + // Destructuring from a simple array -> include undefined + const [s1] = strArray; + s1.toString(); // Should error, s1 possibly undefined + + // Destructuring a rest element -> do not include undefined + const [...s2] = strArray; + s2.push(undefined); // Should error, 'undefined' not part of s2's element type + ~~~~~~~~~ +!!! error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'string'. + + // Destructuring a rest element -> do not include undefined + const [, , ...s3] = strArray; + s3.push(undefined); // Should error, 'undefined' not part of s2's element type + ~~~~~~~~~ +!!! error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'string'. + + // Declaration forms for object destructuring + + declare const strMap: { [s: string]: string }; + + const { t1 } = strMap; + t1.toString(); // Should error, t1 possibly undefined + + const { ...t2 } = strMap; + t2.z.toString(); // Should error + + // Test intersections with declared properties + declare const numMapPoint: { x: number, y: number} & { [s: string]: number }; + { + const { x, y, z } = numMapPoint; + x.toFixed(); // Should OK + y.toFixed(); // Should OK + z.toFixed(); // Should error + } + + { + const { x, ...q } = numMapPoint; + x.toFixed(); // Should OK + q.y.toFixed(); // Should OK + q.z.toFixed(); // Should error + } + + + declare let target_string: string; + declare let target_string_undef: string | undefined; + declare let target_string_arr: string[]; + + // Assignment forms + [target_string] = strArray; // Should error + [target_string_undef] = strArray; // Should OK + [,,, ...target_string_arr] = strArray; // Should OK + + { + let x: number, y: number, z: number | undefined; + ({ x, y, z } = numMapPoint); // Should OK + + let q: number; + ({ q } = numMapPoint); // Should error + } + \ No newline at end of file diff --git a/tests/baselines/reference/noUncheckedIndexedAccessDestructuring.js b/tests/baselines/reference/noUncheckedIndexedAccessDestructuring.js new file mode 100644 index 0000000000000..fd097ecfc5536 --- /dev/null +++ b/tests/baselines/reference/noUncheckedIndexedAccessDestructuring.js @@ -0,0 +1,112 @@ +//// [noUncheckedIndexedAccessDestructuring.ts] +declare const strArray: string[]; +declare const strStrTuple: [string, string]; + +// Declaration forms for array destructuring + +// Destructuring from a simple array -> include undefined +const [s1] = strArray; +s1.toString(); // Should error, s1 possibly undefined + +// Destructuring a rest element -> do not include undefined +const [...s2] = strArray; +s2.push(undefined); // Should error, 'undefined' not part of s2's element type + +// Destructuring a rest element -> do not include undefined +const [, , ...s3] = strArray; +s3.push(undefined); // Should error, 'undefined' not part of s2's element type + +// Declaration forms for object destructuring + +declare const strMap: { [s: string]: string }; + +const { t1 } = strMap; +t1.toString(); // Should error, t1 possibly undefined + +const { ...t2 } = strMap; +t2.z.toString(); // Should error + +// Test intersections with declared properties +declare const numMapPoint: { x: number, y: number} & { [s: string]: number }; +{ + const { x, y, z } = numMapPoint; + x.toFixed(); // Should OK + y.toFixed(); // Should OK + z.toFixed(); // Should error +} + +{ + const { x, ...q } = numMapPoint; + x.toFixed(); // Should OK + q.y.toFixed(); // Should OK + q.z.toFixed(); // Should error +} + + +declare let target_string: string; +declare let target_string_undef: string | undefined; +declare let target_string_arr: string[]; + +// Assignment forms +[target_string] = strArray; // Should error +[target_string_undef] = strArray; // Should OK +[,,, ...target_string_arr] = strArray; // Should OK + +{ + let x: number, y: number, z: number | undefined; + ({ x, y, z } = numMapPoint); // Should OK + + let q: number; + ({ q } = numMapPoint); // Should error +} + + +//// [noUncheckedIndexedAccessDestructuring.js] +"use strict"; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +// Declaration forms for array destructuring +// Destructuring from a simple array -> include undefined +var s1 = strArray[0]; +s1.toString(); // Should error, s1 possibly undefined +// Destructuring a rest element -> do not include undefined +var s2 = strArray.slice(0); +s2.push(undefined); // Should error, 'undefined' not part of s2's element type +// Destructuring a rest element -> do not include undefined +var s3 = strArray.slice(2); +s3.push(undefined); // Should error, 'undefined' not part of s2's element type +var t1 = strMap.t1; +t1.toString(); // Should error, t1 possibly undefined +var t2 = __rest(strMap, []); +t2.z.toString(); // Should error +{ + var x = numMapPoint.x, y = numMapPoint.y, z = numMapPoint.z; + x.toFixed(); // Should OK + y.toFixed(); // Should OK + z.toFixed(); // Should error +} +{ + var x = numMapPoint.x, q = __rest(numMapPoint, ["x"]); + x.toFixed(); // Should OK + q.y.toFixed(); // Should OK + q.z.toFixed(); // Should error +} +// Assignment forms +target_string = strArray[0]; // Should error +target_string_undef = strArray[0]; // Should OK +target_string_arr = strArray.slice(3); // Should OK +{ + var x = void 0, y = void 0, z = void 0; + (x = numMapPoint.x, y = numMapPoint.y, z = numMapPoint.z); // Should OK + var q = void 0; + (q = numMapPoint.q); // Should error +} diff --git a/tests/baselines/reference/noUncheckedIndexedAccessDestructuring.symbols b/tests/baselines/reference/noUncheckedIndexedAccessDestructuring.symbols new file mode 100644 index 0000000000000..fb168915d7bd4 --- /dev/null +++ b/tests/baselines/reference/noUncheckedIndexedAccessDestructuring.symbols @@ -0,0 +1,161 @@ +=== tests/cases/conformance/pedantic/noUncheckedIndexedAccessDestructuring.ts === +declare const strArray: string[]; +>strArray : Symbol(strArray, Decl(noUncheckedIndexedAccessDestructuring.ts, 0, 13)) + +declare const strStrTuple: [string, string]; +>strStrTuple : Symbol(strStrTuple, Decl(noUncheckedIndexedAccessDestructuring.ts, 1, 13)) + +// Declaration forms for array destructuring + +// Destructuring from a simple array -> include undefined +const [s1] = strArray; +>s1 : Symbol(s1, Decl(noUncheckedIndexedAccessDestructuring.ts, 6, 7)) +>strArray : Symbol(strArray, Decl(noUncheckedIndexedAccessDestructuring.ts, 0, 13)) + +s1.toString(); // Should error, s1 possibly undefined +>s1.toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) +>s1 : Symbol(s1, Decl(noUncheckedIndexedAccessDestructuring.ts, 6, 7)) +>toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) + +// Destructuring a rest element -> do not include undefined +const [...s2] = strArray; +>s2 : Symbol(s2, Decl(noUncheckedIndexedAccessDestructuring.ts, 10, 7)) +>strArray : Symbol(strArray, Decl(noUncheckedIndexedAccessDestructuring.ts, 0, 13)) + +s2.push(undefined); // Should error, 'undefined' not part of s2's element type +>s2.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>s2 : Symbol(s2, Decl(noUncheckedIndexedAccessDestructuring.ts, 10, 7)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>undefined : Symbol(undefined) + +// Destructuring a rest element -> do not include undefined +const [, , ...s3] = strArray; +>s3 : Symbol(s3, Decl(noUncheckedIndexedAccessDestructuring.ts, 14, 10)) +>strArray : Symbol(strArray, Decl(noUncheckedIndexedAccessDestructuring.ts, 0, 13)) + +s3.push(undefined); // Should error, 'undefined' not part of s2's element type +>s3.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>s3 : Symbol(s3, Decl(noUncheckedIndexedAccessDestructuring.ts, 14, 10)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>undefined : Symbol(undefined) + +// Declaration forms for object destructuring + +declare const strMap: { [s: string]: string }; +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccessDestructuring.ts, 19, 13)) +>s : Symbol(s, Decl(noUncheckedIndexedAccessDestructuring.ts, 19, 25)) + +const { t1 } = strMap; +>t1 : Symbol(t1, Decl(noUncheckedIndexedAccessDestructuring.ts, 21, 7)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccessDestructuring.ts, 19, 13)) + +t1.toString(); // Should error, t1 possibly undefined +>t1.toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) +>t1 : Symbol(t1, Decl(noUncheckedIndexedAccessDestructuring.ts, 21, 7)) +>toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) + +const { ...t2 } = strMap; +>t2 : Symbol(t2, Decl(noUncheckedIndexedAccessDestructuring.ts, 24, 7)) +>strMap : Symbol(strMap, Decl(noUncheckedIndexedAccessDestructuring.ts, 19, 13)) + +t2.z.toString(); // Should error +>t2.z.toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) +>t2 : Symbol(t2, Decl(noUncheckedIndexedAccessDestructuring.ts, 24, 7)) +>toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --)) + +// Test intersections with declared properties +declare const numMapPoint: { x: number, y: number} & { [s: string]: number }; +>numMapPoint : Symbol(numMapPoint, Decl(noUncheckedIndexedAccessDestructuring.ts, 28, 13)) +>x : Symbol(x, Decl(noUncheckedIndexedAccessDestructuring.ts, 28, 28)) +>y : Symbol(y, Decl(noUncheckedIndexedAccessDestructuring.ts, 28, 39)) +>s : Symbol(s, Decl(noUncheckedIndexedAccessDestructuring.ts, 28, 56)) +{ + const { x, y, z } = numMapPoint; +>x : Symbol(x, Decl(noUncheckedIndexedAccessDestructuring.ts, 30, 11)) +>y : Symbol(y, Decl(noUncheckedIndexedAccessDestructuring.ts, 30, 14)) +>z : Symbol(z, Decl(noUncheckedIndexedAccessDestructuring.ts, 30, 17)) +>numMapPoint : Symbol(numMapPoint, Decl(noUncheckedIndexedAccessDestructuring.ts, 28, 13)) + + x.toFixed(); // Should OK +>x.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(noUncheckedIndexedAccessDestructuring.ts, 30, 11)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) + + y.toFixed(); // Should OK +>y.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>y : Symbol(y, Decl(noUncheckedIndexedAccessDestructuring.ts, 30, 14)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) + + z.toFixed(); // Should error +>z.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>z : Symbol(z, Decl(noUncheckedIndexedAccessDestructuring.ts, 30, 17)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +} + +{ + const { x, ...q } = numMapPoint; +>x : Symbol(x, Decl(noUncheckedIndexedAccessDestructuring.ts, 37, 11)) +>q : Symbol(q, Decl(noUncheckedIndexedAccessDestructuring.ts, 37, 14)) +>numMapPoint : Symbol(numMapPoint, Decl(noUncheckedIndexedAccessDestructuring.ts, 28, 13)) + + x.toFixed(); // Should OK +>x.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(noUncheckedIndexedAccessDestructuring.ts, 37, 11)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) + + q.y.toFixed(); // Should OK +>q.y.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>q.y : Symbol(y, Decl(noUncheckedIndexedAccessDestructuring.ts, 28, 39)) +>q : Symbol(q, Decl(noUncheckedIndexedAccessDestructuring.ts, 37, 14)) +>y : Symbol(y, Decl(noUncheckedIndexedAccessDestructuring.ts, 28, 39)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) + + q.z.toFixed(); // Should error +>q.z.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>q : Symbol(q, Decl(noUncheckedIndexedAccessDestructuring.ts, 37, 14)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +} + + +declare let target_string: string; +>target_string : Symbol(target_string, Decl(noUncheckedIndexedAccessDestructuring.ts, 44, 11)) + +declare let target_string_undef: string | undefined; +>target_string_undef : Symbol(target_string_undef, Decl(noUncheckedIndexedAccessDestructuring.ts, 45, 11)) + +declare let target_string_arr: string[]; +>target_string_arr : Symbol(target_string_arr, Decl(noUncheckedIndexedAccessDestructuring.ts, 46, 11)) + +// Assignment forms +[target_string] = strArray; // Should error +>target_string : Symbol(target_string, Decl(noUncheckedIndexedAccessDestructuring.ts, 44, 11)) +>strArray : Symbol(strArray, Decl(noUncheckedIndexedAccessDestructuring.ts, 0, 13)) + +[target_string_undef] = strArray; // Should OK +>target_string_undef : Symbol(target_string_undef, Decl(noUncheckedIndexedAccessDestructuring.ts, 45, 11)) +>strArray : Symbol(strArray, Decl(noUncheckedIndexedAccessDestructuring.ts, 0, 13)) + +[,,, ...target_string_arr] = strArray; // Should OK +>target_string_arr : Symbol(target_string_arr, Decl(noUncheckedIndexedAccessDestructuring.ts, 46, 11)) +>strArray : Symbol(strArray, Decl(noUncheckedIndexedAccessDestructuring.ts, 0, 13)) + +{ + let x: number, y: number, z: number | undefined; +>x : Symbol(x, Decl(noUncheckedIndexedAccessDestructuring.ts, 54, 7)) +>y : Symbol(y, Decl(noUncheckedIndexedAccessDestructuring.ts, 54, 18)) +>z : Symbol(z, Decl(noUncheckedIndexedAccessDestructuring.ts, 54, 29)) + + ({ x, y, z } = numMapPoint); // Should OK +>x : Symbol(x, Decl(noUncheckedIndexedAccessDestructuring.ts, 55, 6)) +>y : Symbol(y, Decl(noUncheckedIndexedAccessDestructuring.ts, 55, 9)) +>z : Symbol(z, Decl(noUncheckedIndexedAccessDestructuring.ts, 55, 12)) +>numMapPoint : Symbol(numMapPoint, Decl(noUncheckedIndexedAccessDestructuring.ts, 28, 13)) + + let q: number; +>q : Symbol(q, Decl(noUncheckedIndexedAccessDestructuring.ts, 57, 7)) + + ({ q } = numMapPoint); // Should error +>q : Symbol(q, Decl(noUncheckedIndexedAccessDestructuring.ts, 58, 6)) +>numMapPoint : Symbol(numMapPoint, Decl(noUncheckedIndexedAccessDestructuring.ts, 28, 13)) +} + diff --git a/tests/baselines/reference/noUncheckedIndexedAccessDestructuring.types b/tests/baselines/reference/noUncheckedIndexedAccessDestructuring.types new file mode 100644 index 0000000000000..5b28543d0c575 --- /dev/null +++ b/tests/baselines/reference/noUncheckedIndexedAccessDestructuring.types @@ -0,0 +1,194 @@ +=== tests/cases/conformance/pedantic/noUncheckedIndexedAccessDestructuring.ts === +declare const strArray: string[]; +>strArray : string[] + +declare const strStrTuple: [string, string]; +>strStrTuple : [string, string] + +// Declaration forms for array destructuring + +// Destructuring from a simple array -> include undefined +const [s1] = strArray; +>s1 : string +>strArray : string[] + +s1.toString(); // Should error, s1 possibly undefined +>s1.toString() : string +>s1.toString : () => string +>s1 : string +>toString : () => string + +// Destructuring a rest element -> do not include undefined +const [...s2] = strArray; +>s2 : string[] +>strArray : string[] + +s2.push(undefined); // Should error, 'undefined' not part of s2's element type +>s2.push(undefined) : number +>s2.push : (...items: string[]) => number +>s2 : string[] +>push : (...items: string[]) => number +>undefined : undefined + +// Destructuring a rest element -> do not include undefined +const [, , ...s3] = strArray; +> : undefined +> : undefined +>s3 : string[] +>strArray : string[] + +s3.push(undefined); // Should error, 'undefined' not part of s2's element type +>s3.push(undefined) : number +>s3.push : (...items: string[]) => number +>s3 : string[] +>push : (...items: string[]) => number +>undefined : undefined + +// Declaration forms for object destructuring + +declare const strMap: { [s: string]: string }; +>strMap : { [s: string]: string; } +>s : string + +const { t1 } = strMap; +>t1 : string +>strMap : { [s: string]: string; } + +t1.toString(); // Should error, t1 possibly undefined +>t1.toString() : string +>t1.toString : () => string +>t1 : string +>toString : () => string + +const { ...t2 } = strMap; +>t2 : { [s: string]: string; } +>strMap : { [s: string]: string; } + +t2.z.toString(); // Should error +>t2.z.toString() : string +>t2.z.toString : () => string +>t2.z : string +>t2 : { [s: string]: string; } +>z : string +>toString : () => string + +// Test intersections with declared properties +declare const numMapPoint: { x: number, y: number} & { [s: string]: number }; +>numMapPoint : { x: number; y: number; } & { [s: string]: number; } +>x : number +>y : number +>s : string +{ + const { x, y, z } = numMapPoint; +>x : number +>y : number +>z : number +>numMapPoint : { x: number; y: number; } & { [s: string]: number; } + + x.toFixed(); // Should OK +>x.toFixed() : string +>x.toFixed : (fractionDigits?: number | undefined) => string +>x : number +>toFixed : (fractionDigits?: number | undefined) => string + + y.toFixed(); // Should OK +>y.toFixed() : string +>y.toFixed : (fractionDigits?: number | undefined) => string +>y : number +>toFixed : (fractionDigits?: number | undefined) => string + + z.toFixed(); // Should error +>z.toFixed() : string +>z.toFixed : (fractionDigits?: number | undefined) => string +>z : number +>toFixed : (fractionDigits?: number | undefined) => string +} + +{ + const { x, ...q } = numMapPoint; +>x : number +>q : { [s: string]: number; y: number; } +>numMapPoint : { x: number; y: number; } & { [s: string]: number; } + + x.toFixed(); // Should OK +>x.toFixed() : string +>x.toFixed : (fractionDigits?: number | undefined) => string +>x : number +>toFixed : (fractionDigits?: number | undefined) => string + + q.y.toFixed(); // Should OK +>q.y.toFixed() : string +>q.y.toFixed : (fractionDigits?: number | undefined) => string +>q.y : number +>q : { [s: string]: number; y: number; } +>y : number +>toFixed : (fractionDigits?: number | undefined) => string + + q.z.toFixed(); // Should error +>q.z.toFixed() : string +>q.z.toFixed : (fractionDigits?: number | undefined) => string +>q.z : number +>q : { [s: string]: number; y: number; } +>z : number +>toFixed : (fractionDigits?: number | undefined) => string +} + + +declare let target_string: string; +>target_string : string + +declare let target_string_undef: string | undefined; +>target_string_undef : string | undefined + +declare let target_string_arr: string[]; +>target_string_arr : string[] + +// Assignment forms +[target_string] = strArray; // Should error +>[target_string] = strArray : string[] +>[target_string] : [string] +>target_string : string +>strArray : string[] + +[target_string_undef] = strArray; // Should OK +>[target_string_undef] = strArray : string[] +>[target_string_undef] : [string | undefined] +>target_string_undef : string | undefined +>strArray : string[] + +[,,, ...target_string_arr] = strArray; // Should OK +>[,,, ...target_string_arr] = strArray : string[] +>[,,, ...target_string_arr] : [undefined, undefined, undefined, ...string[]] +> : undefined +> : undefined +> : undefined +>...target_string_arr : string +>target_string_arr : string[] +>strArray : string[] + +{ + let x: number, y: number, z: number | undefined; +>x : number +>y : number +>z : number | undefined + + ({ x, y, z } = numMapPoint); // Should OK +>({ x, y, z } = numMapPoint) : { x: number; y: number; } & { [s: string]: number; } +>{ x, y, z } = numMapPoint : { x: number; y: number; } & { [s: string]: number; } +>{ x, y, z } : { x: number; y: number; z: number | undefined; } +>x : number +>y : number +>z : number | undefined +>numMapPoint : { x: number; y: number; } & { [s: string]: number; } + + let q: number; +>q : number + + ({ q } = numMapPoint); // Should error +>({ q } = numMapPoint) : { x: number; y: number; } & { [s: string]: number; } +>{ q } = numMapPoint : { x: number; y: number; } & { [s: string]: number; } +>{ q } : { q: number; } +>q : number +>numMapPoint : { x: number; y: number; } & { [s: string]: number; } +} + diff --git a/tests/baselines/reference/showConfig/Shows tsconfig for single option/noUncheckedIndexedAccess/tsconfig.json b/tests/baselines/reference/showConfig/Shows tsconfig for single option/noUncheckedIndexedAccess/tsconfig.json new file mode 100644 index 0000000000000..32ae1e181cb05 --- /dev/null +++ b/tests/baselines/reference/showConfig/Shows tsconfig for single option/noUncheckedIndexedAccess/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "noUncheckedIndexedAccess": true + } +} diff --git a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json index c9f603c2199ec..5dcbbbcf920d7 100644 --- a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json @@ -39,6 +39,7 @@ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json index 83893736515ea..6584a17cb1c6a 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json @@ -39,6 +39,7 @@ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json index f2ff8ef9f0053..37b49d4c2c32c 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json @@ -39,6 +39,7 @@ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json index 286219dae6e96..fc9500915dbd2 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json @@ -39,6 +39,7 @@ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json index 0ec33da9842fc..46622ef0abb82 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json @@ -39,6 +39,7 @@ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json index ea8f60ca0cf8c..1105491b9adf7 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json @@ -39,6 +39,7 @@ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json index c9f603c2199ec..5dcbbbcf920d7 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json @@ -39,6 +39,7 @@ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json index 7add8d543f8c8..9eb72e47a02e3 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json @@ -39,6 +39,7 @@ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json index 29a0374fa2688..7a9027f49da2d 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json @@ -39,6 +39,7 @@ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/declarationDir-is-specified.js b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/declarationDir-is-specified.js index 68e55e69a289b..fa7dc5795ae36 100644 --- a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/declarationDir-is-specified.js +++ b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/declarationDir-is-specified.js @@ -60,6 +60,7 @@ interface Array { length: number; [n: number]: T; } // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-and-declarationDir-is-specified.js b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-and-declarationDir-is-specified.js index 4f58a6cb9209c..036f14ce4d05a 100644 --- a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-and-declarationDir-is-specified.js +++ b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-and-declarationDir-is-specified.js @@ -60,6 +60,7 @@ interface Array { length: number; [n: number]: T; } // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-is-specified.js b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-is-specified.js index c0390a97918af..7391dc46acc23 100644 --- a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-is-specified.js +++ b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-is-specified.js @@ -60,6 +60,7 @@ interface Array { length: number; [n: number]: T; } // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/with-outFile.js b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/with-outFile.js index b04d25e8fefc4..8c9312cfd0a12 100644 --- a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/with-outFile.js +++ b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/with-outFile.js @@ -60,6 +60,7 @@ interface Array { length: number; [n: number]: T; } // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified-with-declaration-enabled.js b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified-with-declaration-enabled.js index a95b9006f3cbf..5e5f0ccdfc7d5 100644 --- a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified-with-declaration-enabled.js +++ b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified-with-declaration-enabled.js @@ -60,6 +60,7 @@ interface Array { length: number; [n: number]: T; } // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified.js b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified.js index 3b98f66e02fde..832ee4fdbfdc4 100644 --- a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified.js +++ b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified.js @@ -60,6 +60,7 @@ interface Array { length: number; [n: number]: T; } // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts b/tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts new file mode 100644 index 0000000000000..8db2301566feb --- /dev/null +++ b/tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts @@ -0,0 +1,88 @@ +// @strict: true +// @noUncheckedIndexedAccess: true + +type CheckBooleanOnly = any; +// Validate CheckBooleanOnly works - should error +type T_ERR1 = CheckBooleanOnly; + +enum NumericEnum1 { A, B, C } +enum NumericEnum2 { A = 0, B = 1 , C = 2 } +enum StringEnum1 { A = "Alpha", B = "Beta" } + +declare const strMap: { [s: string]: boolean }; + +// All of these should be errors +const e1: boolean = strMap["foo"]; +const e2: boolean = strMap.bar; +const e3: boolean = strMap[0]; +const e4: boolean = strMap[0 as string | number]; +const e5: boolean = strMap[0 as string | 0 | 1]; +const e6: boolean = strMap[0 as 0 | 1]; +const e7: boolean = strMap["foo" as "foo" | "baz"]; +const e8: boolean = strMap[NumericEnum1.A]; +const e9: boolean = strMap[NumericEnum2.A]; +const e10: boolean = strMap[StringEnum1.A]; +const e11: boolean = strMap[StringEnum1.A as StringEnum1]; +const e12: boolean = strMap[NumericEnum1.A as NumericEnum1]; +const e13: boolean = strMap[NumericEnum2.A as NumericEnum2]; +const e14: boolean = strMap[null as any]; + +// Should be OK +const ok1: boolean | undefined = strMap["foo"]; +const ok2: boolean | undefined = strMap.bar; + +type T_OK1 = CheckBooleanOnly<(typeof strMap)[string]>; +type T_OK2 = CheckBooleanOnly<(typeof strMap)["foo"]>; +type T_OK3 = CheckBooleanOnly<(typeof strMap)["bar" | "baz"]>; +type T_OK4 = CheckBooleanOnly<(typeof strMap)[number]>; +type T_OK5 = CheckBooleanOnly<(typeof strMap)[any]>; + +// Writes don't allow 'undefined'; all should be errors +strMap["baz"] = undefined; +strMap.qua = undefined; +strMap[0] = undefined; +strMap[null as any] = undefined; + +// Numeric lookups are unaffected +declare const numMap: { [s: number]: boolean }; +// All of these should be ok +const num_ok1: boolean = numMap[0]; +const num_ok2: boolean = numMap[0 as number]; +const num_ok3: boolean = numMap[0 as 0 | 1]; +const num_ok4: boolean = numMap[NumericEnum1.A]; +const num_ok5: boolean = numMap[NumericEnum2.A]; + +// Generics +function generic1(arg: T): boolean { + // Should error + return arg["blah"]; +} +function generic2(arg: T): boolean { + // Should OK + return arg["blah"]!; +} +function generic3(arg: T): boolean { + // Should error + return strMap[arg]; +} + +// Element access into known properties is ok +declare const obj1: { x: string, y: number, [key: string]: string | number }; +obj1["x"]; +const y = "y"; +obj1[y]; +let yy = "y"; +obj1[yy]; +let z = "z"; +obj1[z]; + +// Distributivity cases +declare const strMapUnion: { [s: string]: boolean } | { [s: string]: number }; +// Should error +const f1: boolean | number = strMapUnion["foo"]; + +// Symbol index signatures +declare const s: unique symbol; +declare const symbolMap: { [s]: string }; +const e15: string = symbolMap[s]; // Should OK +symbolMap[s] = undefined; // Should error diff --git a/tests/cases/conformance/pedantic/noUncheckedIndexedAccessDestructuring.ts b/tests/cases/conformance/pedantic/noUncheckedIndexedAccessDestructuring.ts new file mode 100644 index 0000000000000..8f50f31c5a9a0 --- /dev/null +++ b/tests/cases/conformance/pedantic/noUncheckedIndexedAccessDestructuring.ts @@ -0,0 +1,63 @@ +// @strict: true +// @noUncheckedIndexedAccess: true + +declare const strArray: string[]; +declare const strStrTuple: [string, string]; + +// Declaration forms for array destructuring + +// Destructuring from a simple array -> include undefined +const [s1] = strArray; +s1.toString(); // Should error, s1 possibly undefined + +// Destructuring a rest element -> do not include undefined +const [...s2] = strArray; +s2.push(undefined); // Should error, 'undefined' not part of s2's element type + +// Destructuring a rest element -> do not include undefined +const [, , ...s3] = strArray; +s3.push(undefined); // Should error, 'undefined' not part of s2's element type + +// Declaration forms for object destructuring + +declare const strMap: { [s: string]: string }; + +const { t1 } = strMap; +t1.toString(); // Should error, t1 possibly undefined + +const { ...t2 } = strMap; +t2.z.toString(); // Should error + +// Test intersections with declared properties +declare const numMapPoint: { x: number, y: number} & { [s: string]: number }; +{ + const { x, y, z } = numMapPoint; + x.toFixed(); // Should OK + y.toFixed(); // Should OK + z.toFixed(); // Should error +} + +{ + const { x, ...q } = numMapPoint; + x.toFixed(); // Should OK + q.y.toFixed(); // Should OK + q.z.toFixed(); // Should error +} + + +declare let target_string: string; +declare let target_string_undef: string | undefined; +declare let target_string_arr: string[]; + +// Assignment forms +[target_string] = strArray; // Should error +[target_string_undef] = strArray; // Should OK +[,,, ...target_string_arr] = strArray; // Should OK + +{ + let x: number, y: number, z: number | undefined; + ({ x, y, z } = numMapPoint); // Should OK + + let q: number; + ({ q } = numMapPoint); // Should error +}