diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index f72f30b226c..ef22ec250b3 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -236,10 +236,7 @@ namespace ts { } }); - if (oldCompilerOptions && - (oldCompilerOptions.outDir !== compilerOptions.outDir || - oldCompilerOptions.declarationDir !== compilerOptions.declarationDir || - (oldCompilerOptions.outFile || oldCompilerOptions.out) !== (compilerOptions.outFile || compilerOptions.out))) { + if (oldCompilerOptions && compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions)) { // Add all files to affectedFilesPendingEmit since emit changed state.affectedFilesPendingEmit = concatenate(state.affectedFilesPendingEmit, newProgram.getSourceFiles().map(f => f.path)); if (state.affectedFilesPendingEmitIndex === undefined) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b8ac6aeaf50..71c90ce7fc9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -114,6 +114,11 @@ namespace ts { getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"), getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + symbolCount, getTypeCount: () => typeCount, + getRelationCacheSizes: () => ({ + assignable: assignableRelation.size, + identity: identityRelation.size, + subtype: subtypeRelation.size, + }), isUndefinedSymbol: symbol => symbol === undefinedSymbol, isArgumentsSymbol: symbol => symbol === argumentsSymbol, isUnknownSymbol: symbol => symbol === unknownSymbol, @@ -7474,6 +7479,25 @@ namespace ts { return type.resolvedProperties; } + function getPossiblePropertiesOfUnionType(type: UnionType): Symbol[] { + if (type.possiblePropertyCache) { + return type.possiblePropertyCache.size ? arrayFrom(type.possiblePropertyCache.values()) : emptyArray; + } + type.possiblePropertyCache = createSymbolTable(); + for (const t of type.types) { + for (const p of getPropertiesOfType(t)) { + if (!type.possiblePropertyCache.has(p.escapedName)) { + const prop = getUnionOrIntersectionProperty(type, p.escapedName); + if (prop) { + type.possiblePropertyCache.set(p.escapedName, prop); + } + } + } + } + // We can't simply use the normal property cache here, since that will contain cached apparent type members :( + return type.possiblePropertyCache.size ? arrayFrom(type.possiblePropertyCache.values()) : emptyArray; + } + function getPropertiesOfType(type: Type): Symbol[] { type = getApparentType(type); return type.flags & TypeFlags.UnionOrIntersection ? @@ -7831,7 +7855,7 @@ namespace ts { const isUnion = containingType.flags & TypeFlags.Union; const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0; // Flags we want to propagate to the result if they exist in all source symbols - let commonFlags = isUnion ? SymbolFlags.None : SymbolFlags.Optional; + let optionalFlag = isUnion ? SymbolFlags.None : SymbolFlags.Optional; let syntheticFlag = CheckFlags.SyntheticMethod; let checkFlags = 0; for (const current of containingType.types) { @@ -7840,7 +7864,12 @@ namespace ts { const prop = getPropertyOfType(type, name); const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0; if (prop && !(modifiers & excludeModifiers)) { - commonFlags &= prop.flags; + if (isUnion) { + optionalFlag |= (prop.flags & SymbolFlags.Optional); + } + else { + optionalFlag &= prop.flags; + } const id = "" + getSymbolId(prop); if (!propSet.has(id)) { propSet.set(id, prop); @@ -7858,10 +7887,11 @@ namespace ts { const indexInfo = !isLateBoundName(name) && (isNumericLiteralName(name) && getIndexInfoOfType(type, IndexKind.Number) || getIndexInfoOfType(type, IndexKind.String)); if (indexInfo) { checkFlags |= indexInfo.isReadonly ? CheckFlags.Readonly : 0; + checkFlags |= CheckFlags.WritePartial; indexTypes = append(indexTypes, isTupleType(type) ? getRestTypeOfTupleType(type) || undefinedType : indexInfo.type); } else { - checkFlags |= CheckFlags.Partial; + checkFlags |= CheckFlags.ReadPartial; } } } @@ -7870,7 +7900,7 @@ namespace ts { return undefined; } const props = arrayFrom(propSet.values()); - if (props.length === 1 && !(checkFlags & CheckFlags.Partial) && !indexTypes) { + if (props.length === 1 && !(checkFlags & CheckFlags.ReadPartial) && !indexTypes) { return props[0]; } let declarations: Declaration[] | undefined; @@ -7901,7 +7931,7 @@ namespace ts { propTypes.push(type); } addRange(propTypes, indexTypes); - const result = createSymbol(SymbolFlags.Property | commonFlags, name, syntheticFlag | checkFlags); + const result = createSymbol(SymbolFlags.Property | optionalFlag, name, syntheticFlag | checkFlags); result.containingType = containingType; if (!hasNonUniformValueDeclaration && firstValueDeclaration) { result.valueDeclaration = firstValueDeclaration; @@ -7938,7 +7968,7 @@ namespace ts { function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: __String): Symbol | undefined { const property = getUnionOrIntersectionProperty(type, name); // We need to filter out partial properties in union types - return property && !(getCheckFlags(property) & CheckFlags.Partial) ? property : undefined; + return property && !(getCheckFlags(property) & CheckFlags.ReadPartial) ? property : undefined; } /** @@ -12277,25 +12307,6 @@ namespace ts { return true; } - function isUnionOrIntersectionTypeWithoutNullableConstituents(type: Type): boolean { - if (!(type.flags & TypeFlags.UnionOrIntersection)) { - return false; - } - // at this point we know that this is union or intersection type possibly with nullable constituents. - // check if we still will have compound type if we ignore nullable components. - let seenNonNullable = false; - for (const t of (type).types) { - if (t.flags & TypeFlags.Nullable) { - continue; - } - if (seenNonNullable) { - return true; - } - seenNonNullable = true; - } - return false; - } - /** * Compare two types and return * * Ternary.True if they are related with no assumptions, @@ -12350,7 +12361,8 @@ namespace ts { isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); - if (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral) { + const isPerformingExcessPropertyChecks = (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral); + if (isPerformingExcessPropertyChecks) { const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined; if (hasExcessProperties(source, target, discriminantType, reportErrors)) { if (reportErrors) { @@ -12358,13 +12370,6 @@ namespace ts { } return Ternary.False; } - // Above we check for excess properties with respect to the entire target type. When union - // and intersection types are further deconstructed on the target side, we don't want to - // make the check again (as it might fail for a partial target type). Therefore we obtain - // the regular source type and proceed with that. - if (isUnionOrIntersectionTypeWithoutNullableConstituents(target) && !discriminantType) { - source = getRegularTypeOfObjectLiteral(source); - } } if (relation !== comparableRelation && !isApparentIntersectionConstituent && @@ -12400,11 +12405,24 @@ namespace ts { } else { if (target.flags & TypeFlags.Union) { - result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive)); + result = typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive)); + if (result && isPerformingExcessPropertyChecks) { + // Validate against excess props using the original `source` + const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined; + if (!propertiesRelatedTo(source, discriminantType || target, reportErrors)) { + return Ternary.False; + } + } } else if (target.flags & TypeFlags.Intersection) { isIntersectionConstituent = true; // set here to affect the following trio of checks - result = typeRelatedToEachType(source, target as IntersectionType, reportErrors); + result = typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors); + if (result && isPerformingExcessPropertyChecks) { + // Validate against excess props using the original `source` + if (!propertiesRelatedTo(source, target, reportErrors)) { + return Ternary.False; + } + } } else if (source.flags & TypeFlags.Intersection) { // Check to see if any constituents of the intersection are immediately related to the target. @@ -12507,7 +12525,7 @@ namespace ts { // check excess properties against discriminant type only, not the entire union return hasExcessProperties(source, discriminant, /*discriminant*/ undefined, reportErrors); } - for (const prop of getPropertiesOfObjectType(source)) { + for (const prop of getPropertiesOfType(source)) { if (shouldCheckAsExcessProperty(prop, source.symbol) && !isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) { if (reportErrors) { // Report error in terms of object types in the target as those are the only ones @@ -13234,7 +13252,9 @@ namespace ts { } } } - const properties = getPropertiesOfObjectType(target); + // We only call this for union target types when we're attempting to do excess property checking - in those cases, we want to get _all possible props_ + // from the target union, across all members + const properties = target.flags & TypeFlags.Union ? getPossiblePropertiesOfUnionType(target as UnionType) : getPropertiesOfType(target); for (const targetProp of properties) { if (!(targetProp.flags & SymbolFlags.Prototype)) { const sourceProp = getPropertyOfType(source, targetProp.escapedName); @@ -13282,7 +13302,8 @@ namespace ts { } return Ternary.False; } - const related = isRelatedTo(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors); + // If the target comes from a partial union prop, allow `undefined` in the target type + const related = isRelatedTo(getTypeOfSymbol(sourceProp), addOptionality(getTypeOfSymbol(targetProp), !!(getCheckFlags(targetProp) & CheckFlags.Partial)), reportErrors); if (!related) { if (reportErrors) { reportError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp)); @@ -14628,9 +14649,9 @@ namespace ts { } function* getUnmatchedProperties(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean) { - const properties = target.flags & TypeFlags.Intersection ? getPropertiesOfUnionOrIntersectionType(target) : getPropertiesOfObjectType(target); + const properties = target.flags & TypeFlags.Union ? getPossiblePropertiesOfUnionType(target as UnionType) : getPropertiesOfType(target); for (const targetProp of properties) { - if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional)) { + if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional || getCheckFlags(targetProp) & CheckFlags.Partial)) { const sourceProp = getPropertyOfType(source, targetProp.escapedName); if (!sourceProp) { yield targetProp; @@ -15001,12 +15022,11 @@ namespace ts { } // If no inferences can be made to K's constraint, infer from a union of the property types // in the source to the template type X. - const valueTypes = compact([ - getIndexTypeOfType(source, IndexKind.String), - getIndexTypeOfType(source, IndexKind.Number), - ...map(getPropertiesOfType(source), getTypeOfSymbol) - ]); - inferFromTypes(getUnionType(valueTypes), getTemplateTypeFromMappedType(target)); + const propTypes = map(getPropertiesOfType(source), getTypeOfSymbol); + const stringIndexType = getIndexTypeOfType(source, IndexKind.String); + const numberIndexInfo = getNonEnumNumberIndexInfo(source); + const numberIndexType = numberIndexInfo && numberIndexInfo.type; + inferFromTypes(getUnionType(append(append(propTypes, stringIndexType), numberIndexType)), getTemplateTypeFromMappedType(target)); return true; } return false; diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 12a20e0df42..edb25e9cb71 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -281,6 +281,7 @@ namespace ts { name: "declaration", shortName: "d", type: "boolean", + affectsEmit: true, showInSimplifiedHelpView: true, category: Diagnostics.Basic_Options, description: Diagnostics.Generates_corresponding_d_ts_file, @@ -288,6 +289,7 @@ namespace ts { { name: "declarationMap", type: "boolean", + affectsEmit: true, showInSimplifiedHelpView: true, category: Diagnostics.Basic_Options, description: Diagnostics.Generates_a_sourcemap_for_each_corresponding_d_ts_file, @@ -295,12 +297,14 @@ namespace ts { { name: "emitDeclarationOnly", type: "boolean", + affectsEmit: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Only_emit_d_ts_declaration_files, }, { name: "sourceMap", type: "boolean", + affectsEmit: true, showInSimplifiedHelpView: true, category: Diagnostics.Basic_Options, description: Diagnostics.Generates_corresponding_map_file, @@ -308,6 +312,7 @@ namespace ts { { name: "outFile", type: "string", + affectsEmit: true, isFilePath: true, paramType: Diagnostics.FILE, showInSimplifiedHelpView: true, @@ -317,6 +322,7 @@ namespace ts { { name: "outDir", type: "string", + affectsEmit: true, isFilePath: true, paramType: Diagnostics.DIRECTORY, showInSimplifiedHelpView: true, @@ -326,6 +332,7 @@ namespace ts { { name: "rootDir", type: "string", + affectsEmit: true, isFilePath: true, paramType: Diagnostics.LOCATION, category: Diagnostics.Basic_Options, @@ -334,6 +341,7 @@ namespace ts { { name: "composite", type: "boolean", + affectsEmit: true, isTSConfigOnly: true, category: Diagnostics.Basic_Options, description: Diagnostics.Enable_project_compilation, @@ -341,6 +349,7 @@ namespace ts { { name: "tsBuildInfoFile", type: "string", + affectsEmit: true, isFilePath: true, paramType: Diagnostics.FILE, category: Diagnostics.Basic_Options, @@ -349,6 +358,7 @@ namespace ts { { name: "removeComments", type: "boolean", + affectsEmit: true, showInSimplifiedHelpView: true, category: Diagnostics.Basic_Options, description: Diagnostics.Do_not_emit_comments_to_output, @@ -356,6 +366,7 @@ namespace ts { { name: "noEmit", type: "boolean", + affectsEmit: true, showInSimplifiedHelpView: true, category: Diagnostics.Basic_Options, description: Diagnostics.Do_not_emit_outputs, @@ -363,12 +374,14 @@ namespace ts { { name: "importHelpers", type: "boolean", + affectsEmit: true, category: Diagnostics.Basic_Options, description: Diagnostics.Import_emit_helpers_from_tslib }, { name: "downlevelIteration", type: "boolean", + affectsEmit: true, category: Diagnostics.Basic_Options, description: Diagnostics.Provide_full_support_for_iterables_in_for_of_spread_and_destructuring_when_targeting_ES5_or_ES3 }, @@ -580,6 +593,7 @@ namespace ts { { name: "sourceRoot", type: "string", + affectsEmit: true, paramType: Diagnostics.LOCATION, category: Diagnostics.Source_Map_Options, description: Diagnostics.Specify_the_location_where_debugger_should_locate_TypeScript_files_instead_of_source_locations, @@ -587,6 +601,7 @@ namespace ts { { name: "mapRoot", type: "string", + affectsEmit: true, paramType: Diagnostics.LOCATION, category: Diagnostics.Source_Map_Options, description: Diagnostics.Specify_the_location_where_debugger_should_locate_map_files_instead_of_generated_locations, @@ -594,12 +609,14 @@ namespace ts { { name: "inlineSourceMap", type: "boolean", + affectsEmit: true, category: Diagnostics.Source_Map_Options, description: Diagnostics.Emit_a_single_file_with_source_maps_instead_of_having_a_separate_file }, { name: "inlineSources", type: "boolean", + affectsEmit: true, category: Diagnostics.Source_Map_Options, description: Diagnostics.Emit_the_source_alongside_the_sourcemaps_within_a_single_file_requires_inlineSourceMap_or_sourceMap_to_be_set }, @@ -635,6 +652,7 @@ namespace ts { { name: "out", type: "string", + affectsEmit: true, isFilePath: false, // This is intentionally broken to support compatability with existing tsconfig files // for correct behaviour, please use outFile category: Diagnostics.Advanced_Options, @@ -644,6 +662,7 @@ namespace ts { { name: "reactNamespace", type: "string", + affectsEmit: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Deprecated_Use_jsxFactory_instead_Specify_the_object_invoked_for_createElement_when_targeting_react_JSX_emit }, @@ -662,6 +681,7 @@ namespace ts { { name: "emitBOM", type: "boolean", + affectsEmit: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Emit_a_UTF_8_Byte_Order_Mark_BOM_in_the_beginning_of_output_files }, @@ -677,6 +697,7 @@ namespace ts { crlf: NewLineKind.CarriageReturnLineFeed, lf: NewLineKind.LineFeed }), + affectsEmit: true, paramType: Diagnostics.NEWLINE, category: Diagnostics.Advanced_Options, description: Diagnostics.Specify_the_end_of_line_sequence_to_be_used_when_emitting_files_Colon_CRLF_dos_or_LF_unix, @@ -704,6 +725,7 @@ namespace ts { { name: "stripInternal", type: "boolean", + affectsEmit: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Do_not_emit_declarations_for_code_that_has_an_internal_annotation, }, @@ -724,24 +746,28 @@ namespace ts { { name: "noEmitHelpers", type: "boolean", + affectsEmit: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Do_not_generate_custom_helper_functions_like_extends_in_compiled_output }, { name: "noEmitOnError", type: "boolean", + affectsEmit: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Do_not_emit_outputs_if_any_errors_were_reported, }, { name: "preserveConstEnums", type: "boolean", + affectsEmit: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Do_not_erase_const_enum_declarations_in_generated_code }, { name: "declarationDir", type: "string", + affectsEmit: true, isFilePath: true, paramType: Diagnostics.DIRECTORY, category: Diagnostics.Advanced_Options, @@ -826,6 +852,10 @@ namespace ts { export const semanticDiagnosticsOptionDeclarations: ReadonlyArray = optionDeclarations.filter(option => !!option.affectsSemanticDiagnostics); + /* @internal */ + export const affectsEmitOptionDeclarations: ReadonlyArray = + optionDeclarations.filter(option => !!option.affectsEmit); + /* @internal */ export const moduleResolutionOptionDeclarations: ReadonlyArray = optionDeclarations.filter(option => !!option.affectsModuleResolution); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 74bb8e1b1c8..4f50aa3ab1f 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -927,6 +927,7 @@ namespace ts { getIdentifierCount: () => getDiagnosticsProducingTypeChecker().getIdentifierCount(), getSymbolCount: () => getDiagnosticsProducingTypeChecker().getSymbolCount(), getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(), + getRelationCacheSizes: () => getDiagnosticsProducingTypeChecker().getRelationCacheSizes(), getFileProcessingDiagnostics: () => fileProcessingDiagnostics, getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives, isSourceFileFromExternalLibrary, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 23d72875e4f..f9ad5ba1570 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2961,6 +2961,7 @@ namespace ts { /* @internal */ getIdentifierCount(): number; /* @internal */ getSymbolCount(): number; /* @internal */ getTypeCount(): number; + /* @internal */ getRelationCacheSizes(): { assignable: number, identity: number, subtype: number }; /* @internal */ getFileProcessingDiagnostics(): DiagnosticCollection; /* @internal */ getResolvedTypeReferenceDirectives(): Map; @@ -3246,6 +3247,7 @@ namespace ts { /* @internal */ getIdentifierCount(): number; /* @internal */ getSymbolCount(): number; /* @internal */ getTypeCount(): number; + /* @internal */ getRelationCacheSizes(): { assignable: number, identity: number, subtype: number }; /* @internal */ isArrayType(type: Type): boolean; /* @internal */ isTupleType(type: Type): boolean; @@ -3746,19 +3748,21 @@ namespace ts { SyntheticProperty = 1 << 1, // Property in union or intersection type SyntheticMethod = 1 << 2, // Method in union or intersection type Readonly = 1 << 3, // Readonly transient symbol - Partial = 1 << 4, // Synthetic property present in some but not all constituents - HasNonUniformType = 1 << 5, // Synthetic property with non-uniform type in constituents - HasLiteralType = 1 << 6, // Synthetic property with at least one literal type in constituents - ContainsPublic = 1 << 7, // Synthetic property with public constituent(s) - ContainsProtected = 1 << 8, // Synthetic property with protected constituent(s) - ContainsPrivate = 1 << 9, // Synthetic property with private constituent(s) - ContainsStatic = 1 << 10, // Synthetic property with static constituent(s) - Late = 1 << 11, // Late-bound symbol for a computed property with a dynamic name - ReverseMapped = 1 << 12, // Property of reverse-inferred homomorphic mapped type - OptionalParameter = 1 << 13, // Optional parameter - RestParameter = 1 << 14, // Rest parameter + ReadPartial = 1 << 4, // Synthetic property present in some but not all constituents + WritePartial = 1 << 5, // Synthetic property present in some but only satisfied by an index signature in others + HasNonUniformType = 1 << 6, // Synthetic property with non-uniform type in constituents + HasLiteralType = 1 << 7, // Synthetic property with at least one literal type in constituents + ContainsPublic = 1 << 8, // Synthetic property with public constituent(s) + ContainsProtected = 1 << 9, // Synthetic property with protected constituent(s) + ContainsPrivate = 1 << 10, // Synthetic property with private constituent(s) + ContainsStatic = 1 << 11, // Synthetic property with static constituent(s) + Late = 1 << 12, // Late-bound symbol for a computed property with a dynamic name + ReverseMapped = 1 << 13, // Property of reverse-inferred homomorphic mapped type + OptionalParameter = 1 << 14, // Optional parameter + RestParameter = 1 << 15, // Rest parameter Synthetic = SyntheticProperty | SyntheticMethod, - Discriminant = HasNonUniformType | HasLiteralType + Discriminant = HasNonUniformType | HasLiteralType, + Partial = ReadPartial | WritePartial } /* @internal */ @@ -4171,6 +4175,8 @@ namespace ts { } export interface UnionType extends UnionOrIntersectionType { + /* @internal */ + possiblePropertyCache?: SymbolTable; // Cache of _all_ resolved properties less any from aparent members } export interface IntersectionType extends UnionOrIntersectionType { @@ -4823,6 +4829,7 @@ namespace ts { affectsModuleResolution?: true; // currently same effect as `affectsSourceFile` affectsBindDiagnostics?: true; // true if this affects binding (currently same effect as `affectsSourceFile`) affectsSemanticDiagnostics?: true; // true if option affects semantic diagnostics + affectsEmit?: true; // true if the options affects emit } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index eca7dc7aee9..2e6c3ae4f86 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -7320,6 +7320,11 @@ namespace ts { semanticDiagnosticsOptionDeclarations.some(option => !isJsonEqual(getCompilerOptionValue(oldOptions, option), getCompilerOptionValue(newOptions, option))); } + export function compilerOptionsAffectEmit(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean { + return oldOptions !== newOptions && + affectsEmitOptionDeclarations.some(option => !isJsonEqual(getCompilerOptionValue(oldOptions, option), getCompilerOptionValue(newOptions, option))); + } + export function getCompilerOptionValue(options: CompilerOptions, option: CommandLineOption): unknown { return option.strictFlag ? getStrictOptionValue(options, option.name as StrictOptionName) : options[option.name]; } diff --git a/src/testRunner/unittests/tsbuild/sample.ts b/src/testRunner/unittests/tsbuild/sample.ts index 588eb256eda..ab9210451aa 100644 --- a/src/testRunner/unittests/tsbuild/sample.ts +++ b/src/testRunner/unittests/tsbuild/sample.ts @@ -720,6 +720,47 @@ class someClass { }`), "/src/tests/tsconfig.tsbuildinfo", ] }); + + verifyTsbuildOutput({ + scenario: "when declaration option changes", + projFs: () => projFs, + time, + tick, + proj: "sample1", + rootNames: ["/src/core"], + expectedMapFileNames: emptyArray, + lastProjectOutputJs: "/src/core/index.js", + initialBuild: { + modifyFs: fs => fs.writeFileSync("/src/core/tsconfig.json", `{ + "compilerOptions": { + "incremental": true, + "skipDefaultLibCheck": true + } +}`), + expectedDiagnostics: [ + getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json"), + [Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"], + [Diagnostics.Building_project_0, "/src/core/tsconfig.json"], + ] + }, + incrementalDtsChangedBuild: { + modifyFs: fs => replaceText(fs, "/src/core/tsconfig.json", `"incremental": true,`, `"incremental": true, "declaration": true,`), + expectedDiagnostics: [ + getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json"), + [Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.d.ts"], + [Diagnostics.Building_project_0, "/src/core/tsconfig.json"] + ] + }, + outputFiles: [ + "/src/core/anotherModule.js", + "/src/core/anotherModule.d.ts", + "/src/core/index.js", + "/src/core/index.d.ts", + "/src/core/tsconfig.tsbuildinfo", + ], + baselineOnly: true, + verifyDiagnostics: true + }); }); }); } diff --git a/src/tsc/tsc.ts b/src/tsc/tsc.ts index 984ebe21cf6..500480463bd 100644 --- a/src/tsc/tsc.ts +++ b/src/tsc/tsc.ts @@ -340,6 +340,10 @@ namespace ts { const checkTime = performance.getDuration("Check"); const emitTime = performance.getDuration("Emit"); if (compilerOptions.extendedDiagnostics) { + const caches = program.getRelationCacheSizes(); + reportCountStatistic("Assignability cache size", caches.assignable); + reportCountStatistic("Identity cache size", caches.identity); + reportCountStatistic("Subtype cache size", caches.subtype); performance.forEachMeasure((name, duration) => reportTimeStatistic(`${name} time`, duration)); } else { diff --git a/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.errors.txt b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.errors.txt new file mode 100644 index 00000000000..6a83638ba90 --- /dev/null +++ b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.errors.txt @@ -0,0 +1,48 @@ +tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts(21,33): error TS2322: Type '{ INVALID_PROP_NAME: string; ariaLabel: string; }' is not assignable to type 'ITestProps'. + Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'ITestProps'. +tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts(27,34): error TS2326: Types of property 'icon' are incompatible. + Type '{ props: { INVALID_PROP_NAME: string; ariaLabel: string; }; }' is not assignable to type 'NestedProp'. + Types of property 'props' are incompatible. + Type '{ INVALID_PROP_NAME: string; ariaLabel: string; }' is not assignable to type 'ITestProps'. + Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'ITestProps'. + + +==== tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts (2 errors) ==== + interface StatelessComponent

{ + (props: P & { children?: number }, context?: any): null; + } + + const TestComponent: StatelessComponent = (props) => { + return null; + } + + interface ITestProps { + ariaLabel?: string; + } + + interface NestedProp { + props: TProps; + } + + interface TestProps { + icon: NestedProp; + } + + TestComponent({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); + ~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ INVALID_PROP_NAME: string; ariaLabel: string; }' is not assignable to type 'ITestProps'. +!!! error TS2322: Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'ITestProps'. +!!! related TS6500 tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts:14:3: The expected type comes from property 'props' which is declared here on type 'NestedProp' + + const TestComponent2: StatelessComponent = (props) => { + return null; + } + + TestComponent2({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); + ~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2326: Types of property 'icon' are incompatible. +!!! error TS2326: Type '{ props: { INVALID_PROP_NAME: string; ariaLabel: string; }; }' is not assignable to type 'NestedProp'. +!!! error TS2326: Types of property 'props' are incompatible. +!!! error TS2326: Type '{ INVALID_PROP_NAME: string; ariaLabel: string; }' is not assignable to type 'ITestProps'. +!!! error TS2326: Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'ITestProps'. + \ No newline at end of file diff --git a/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.js b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.js new file mode 100644 index 00000000000..cb0e6503b79 --- /dev/null +++ b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.js @@ -0,0 +1,39 @@ +//// [deepExcessPropertyCheckingWhenTargetIsIntersection.ts] +interface StatelessComponent

{ + (props: P & { children?: number }, context?: any): null; +} + +const TestComponent: StatelessComponent = (props) => { + return null; +} + +interface ITestProps { + ariaLabel?: string; +} + +interface NestedProp { + props: TProps; +} + +interface TestProps { + icon: NestedProp; +} + +TestComponent({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); + +const TestComponent2: StatelessComponent = (props) => { + return null; +} + +TestComponent2({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); + + +//// [deepExcessPropertyCheckingWhenTargetIsIntersection.js] +var TestComponent = function (props) { + return null; +}; +TestComponent({ icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } } }); +var TestComponent2 = function (props) { + return null; +}; +TestComponent2({ icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } } }); diff --git a/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.symbols b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.symbols new file mode 100644 index 00000000000..717cb872cec --- /dev/null +++ b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.symbols @@ -0,0 +1,71 @@ +=== tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts === +interface StatelessComponent

{ +>StatelessComponent : Symbol(StatelessComponent, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 0, 0)) +>P : Symbol(P, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 0, 29)) + + (props: P & { children?: number }, context?: any): null; +>props : Symbol(props, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 1, 3)) +>P : Symbol(P, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 0, 29)) +>children : Symbol(children, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 1, 15)) +>context : Symbol(context, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 1, 36)) +} + +const TestComponent: StatelessComponent = (props) => { +>TestComponent : Symbol(TestComponent, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 4, 5)) +>StatelessComponent : Symbol(StatelessComponent, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 0, 0)) +>TestProps : Symbol(TestProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 14, 1)) +>props : Symbol(props, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 4, 54)) + + return null; +} + +interface ITestProps { +>ITestProps : Symbol(ITestProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 6, 1)) + + ariaLabel?: string; +>ariaLabel : Symbol(ITestProps.ariaLabel, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 8, 22)) +} + +interface NestedProp { +>NestedProp : Symbol(NestedProp, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 10, 1)) +>TProps : Symbol(TProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 12, 21)) + + props: TProps; +>props : Symbol(NestedProp.props, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 12, 30)) +>TProps : Symbol(TProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 12, 21)) +} + +interface TestProps { +>TestProps : Symbol(TestProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 14, 1)) + + icon: NestedProp; +>icon : Symbol(TestProps.icon, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 16, 21)) +>NestedProp : Symbol(NestedProp, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 10, 1)) +>ITestProps : Symbol(ITestProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 6, 1)) +} + +TestComponent({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); +>TestComponent : Symbol(TestComponent, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 4, 5)) +>icon : Symbol(icon, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 20, 15)) +>props : Symbol(props, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 20, 22)) +>INVALID_PROP_NAME : Symbol(INVALID_PROP_NAME, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 20, 31)) +>ariaLabel : Symbol(ariaLabel, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 20, 59)) + +const TestComponent2: StatelessComponent = (props) => { +>TestComponent2 : Symbol(TestComponent2, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 22, 5)) +>StatelessComponent : Symbol(StatelessComponent, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 0, 0)) +>TestProps : Symbol(TestProps, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 14, 1)) +>props2 : Symbol(props2, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 22, 54)) +>x : Symbol(x, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 22, 63)) +>props : Symbol(props, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 22, 79)) + + return null; +} + +TestComponent2({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); +>TestComponent2 : Symbol(TestComponent2, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 22, 5)) +>icon : Symbol(icon, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 26, 16)) +>props : Symbol(props, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 26, 23)) +>INVALID_PROP_NAME : Symbol(INVALID_PROP_NAME, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 26, 32)) +>ariaLabel : Symbol(ariaLabel, Decl(deepExcessPropertyCheckingWhenTargetIsIntersection.ts, 26, 60)) + diff --git a/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.types b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.types new file mode 100644 index 00000000000..dc84ff20e08 --- /dev/null +++ b/tests/baselines/reference/deepExcessPropertyCheckingWhenTargetIsIntersection.types @@ -0,0 +1,70 @@ +=== tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts === +interface StatelessComponent

{ + (props: P & { children?: number }, context?: any): null; +>props : P & { children?: number; } +>children : number +>context : any +>null : null +} + +const TestComponent: StatelessComponent = (props) => { +>TestComponent : StatelessComponent +>(props) => { return null;} : (props: TestProps & { children?: number; }) => any +>props : TestProps & { children?: number; } + + return null; +>null : null +} + +interface ITestProps { + ariaLabel?: string; +>ariaLabel : string +} + +interface NestedProp { + props: TProps; +>props : TProps +} + +interface TestProps { + icon: NestedProp; +>icon : NestedProp +} + +TestComponent({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); +>TestComponent({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}) : null +>TestComponent : StatelessComponent +>{icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }} : { icon: { props: { INVALID_PROP_NAME: string; ariaLabel: string; }; }; } +>icon : { props: { INVALID_PROP_NAME: string; ariaLabel: string; }; } +>{ props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } } : { props: { INVALID_PROP_NAME: string; ariaLabel: string; }; } +>props : { INVALID_PROP_NAME: string; ariaLabel: string; } +>{ INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } : { INVALID_PROP_NAME: string; ariaLabel: string; } +>INVALID_PROP_NAME : string +>'share' : "share" +>ariaLabel : string +>'test label' : "test label" + +const TestComponent2: StatelessComponent = (props) => { +>TestComponent2 : StatelessComponent +>props2 : { x: number; } +>x : number +>(props) => { return null;} : (props: (TestProps & { children?: number; }) | ({ props2: { x: number; }; } & { children?: number; })) => any +>props : (TestProps & { children?: number; }) | ({ props2: { x: number; }; } & { children?: number; }) + + return null; +>null : null +} + +TestComponent2({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); +>TestComponent2({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}) : null +>TestComponent2 : StatelessComponent +>{icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }} : { icon: { props: { INVALID_PROP_NAME: string; ariaLabel: string; }; }; } +>icon : { props: { INVALID_PROP_NAME: string; ariaLabel: string; }; } +>{ props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } } : { props: { INVALID_PROP_NAME: string; ariaLabel: string; }; } +>props : { INVALID_PROP_NAME: string; ariaLabel: string; } +>{ INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } : { INVALID_PROP_NAME: string; ariaLabel: string; } +>INVALID_PROP_NAME : string +>'share' : "share" +>ariaLabel : string +>'test label' : "test label" + diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt index 5331add9e8d..469b6d089b6 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt @@ -20,11 +20,9 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(49,35): error TS2322: Type Object literal may only specify known properties, and 'second' does not exist in type '{ a: 1; b: 1; first: string; }'. tests/cases/compiler/excessPropertyCheckWithUnions.ts(50,35): error TS2322: Type '{ a: 1; b: 1; first: string; third: string; }' is not assignable to type 'Overlapping'. Object literal may only specify known properties, and 'third' does not exist in type '{ a: 1; b: 1; first: string; }'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type 'AB'. - Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type '{ kind: "A"; n: AN; }'. - Types of property 'n' are incompatible. - Type '{ a: string; b: string; }' is not assignable to type 'AN'. - Object literal may only specify known properties, and 'b' does not exist in type 'AN'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2326: Types of property 'n' are incompatible. + Type '{ a: string; b: string; }' is not assignable to type 'AN'. + Object literal may only specify known properties, and 'b' does not exist in type 'AN'. ==== tests/cases/compiler/excessPropertyCheckWithUnions.ts (10 errors) ==== @@ -127,11 +125,9 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type a: "a", b: "b", // excess -- kind: "A" ~~~~~~ -!!! error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type 'AB'. -!!! error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type '{ kind: "A"; n: AN; }'. -!!! error TS2322: Types of property 'n' are incompatible. -!!! error TS2322: Type '{ a: string; b: string; }' is not assignable to type 'AN'. -!!! error TS2322: Object literal may only specify known properties, and 'b' does not exist in type 'AN'. +!!! error TS2326: Types of property 'n' are incompatible. +!!! error TS2326: Type '{ a: string; b: string; }' is not assignable to type 'AN'. +!!! error TS2326: Object literal may only specify known properties, and 'b' does not exist in type 'AN'. } } const abac: AB = { diff --git a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.errors.txt b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.errors.txt new file mode 100644 index 00000000000..368a71de003 --- /dev/null +++ b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.errors.txt @@ -0,0 +1,116 @@ +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(18,19): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(19,31): error TS2322: Type '{ x: string; y: number; }' is not assignable to type 'A'. + Object literal may only specify known properties, and 'y' does not exist in type 'A'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(22,19): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(23,31): error TS2322: Type '{ x: string; y: number; }' is not assignable to type 'A'. + Object literal may only specify known properties, and 'y' does not exist in type 'A'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(34,5): error TS2322: Type '{ id: number; url: string; xyz: number; }' is not assignable to type '{ id: number; } & { url: string; }'. + Object literal may only specify known properties, and 'xyz' does not exist in type '{ id: number; } & { url: string; }'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(43,9): error TS2322: Type '{ id: number; url: string; xyz: number; }' is not assignable to type '{ id: number; } & { url: string; }'. + Object literal may only specify known properties, and 'xyz' does not exist in type '{ id: number; } & { url: string; }'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(68,32): error TS2322: Type '{ foo: true; bar: true; boo: boolean; }' is not assignable to type 'View'. + Object literal may only specify known properties, and 'boo' does not exist in type 'View'. +tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts(70,50): error TS2322: Type '{ foo: true; bar: true; boo: boolean; }' is not assignable to type 'boolean | View'. + Object literal may only specify known properties, and 'boo' does not exist in type 'View'. + + +==== tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts (8 errors) ==== + // https://github.com/Microsoft/TypeScript/issues/13813 + + interface A { + x: string + } + + interface B { + a: A; + } + + interface C { + c: number; + } + + type D = B & C; + + let a: B = { a: { x: 'hello' } }; // ok + let b: B = { a: { x: 2 } }; // error - types of property x are incompatible + ~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. +!!! related TS6500 tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts:4:5: The expected type comes from property 'x' which is declared here on type 'A' + let c: B = { a: { x: 'hello', y: 2 } }; // error - y does not exist in type A + ~~~~ +!!! error TS2322: Type '{ x: string; y: number; }' is not assignable to type 'A'. +!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type 'A'. +!!! related TS6500 tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts:8:5: The expected type comes from property 'a' which is declared here on type 'B' + + let d: D = { a: { x: 'hello' }, c: 5 }; // ok + let e: D = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatible + ~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. +!!! related TS6500 tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts:4:5: The expected type comes from property 'x' which is declared here on type 'A' + let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error + ~~~~ +!!! error TS2322: Type '{ x: string; y: number; }' is not assignable to type 'A'. +!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type 'A'. +!!! related TS6500 tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts:8:5: The expected type comes from property 'a' which is declared here on type 'D' + + // https://github.com/Microsoft/TypeScript/issues/18075 + + export type MyType = { id: number; } & { name: string; } & { photo: { id: number; } & { url: string; } } + + export let obj: MyType; + + export const photo: typeof obj.photo = { + id: 1, + url: '', + xyz: 1 // Great! This causes an error! + ~~~~~~ +!!! error TS2322: Type '{ id: number; url: string; xyz: number; }' is not assignable to type '{ id: number; } & { url: string; }'. +!!! error TS2322: Object literal may only specify known properties, and 'xyz' does not exist in type '{ id: number; } & { url: string; }'. + }; + + export const myInstance: MyType = { + id: 1, + name: '', + photo: { + id: 1, + url: '', + xyz: 2 // This should also be an error + ~~~~~~ +!!! error TS2322: Type '{ id: number; url: string; xyz: number; }' is not assignable to type '{ id: number; } & { url: string; }'. +!!! error TS2322: Object literal may only specify known properties, and 'xyz' does not exist in type '{ id: number; } & { url: string; }'. +!!! related TS6500 tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts:27:62: The expected type comes from property 'photo' which is declared here on type 'MyType' + } + }; + + // https://github.com/Microsoft/TypeScript/issues/28616 + + export type View = { [K in keyof T]: T[K] extends object ? boolean | View : boolean }; + + interface TypeC { + foo: string; + bar: string; + } + + interface TypeB { + foo: string, + bar: TypeC + } + + interface TypeA { + foo: string, + bar: TypeB, + } + + let test: View; + + test = { foo: true, bar: true, boo: true } + ~~~~~~~~~ +!!! error TS2322: Type '{ foo: true; bar: true; boo: boolean; }' is not assignable to type 'View'. +!!! error TS2322: Object literal may only specify known properties, and 'boo' does not exist in type 'View'. + + test = { foo: true, bar: { foo: true, bar: true, boo: true } } + ~~~~~~~~~ +!!! error TS2322: Type '{ foo: true; bar: true; boo: boolean; }' is not assignable to type 'boolean | View'. +!!! error TS2322: Object literal may only specify known properties, and 'boo' does not exist in type 'View'. +!!! related TS6500 tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts:63:5: The expected type comes from property 'bar' which is declared here on type 'View' + \ No newline at end of file diff --git a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.js b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.js new file mode 100644 index 00000000000..f43b7bf83a4 --- /dev/null +++ b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.js @@ -0,0 +1,100 @@ +//// [excessPropertyChecksWithNestedIntersections.ts] +// https://github.com/Microsoft/TypeScript/issues/13813 + +interface A { + x: string +} + +interface B { + a: A; +} + +interface C { + c: number; +} + +type D = B & C; + +let a: B = { a: { x: 'hello' } }; // ok +let b: B = { a: { x: 2 } }; // error - types of property x are incompatible +let c: B = { a: { x: 'hello', y: 2 } }; // error - y does not exist in type A + +let d: D = { a: { x: 'hello' }, c: 5 }; // ok +let e: D = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatible +let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error + +// https://github.com/Microsoft/TypeScript/issues/18075 + +export type MyType = { id: number; } & { name: string; } & { photo: { id: number; } & { url: string; } } + +export let obj: MyType; + +export const photo: typeof obj.photo = { + id: 1, + url: '', + xyz: 1 // Great! This causes an error! +}; + +export const myInstance: MyType = { + id: 1, + name: '', + photo: { + id: 1, + url: '', + xyz: 2 // This should also be an error + } +}; + +// https://github.com/Microsoft/TypeScript/issues/28616 + +export type View = { [K in keyof T]: T[K] extends object ? boolean | View : boolean }; + +interface TypeC { + foo: string; + bar: string; +} + +interface TypeB { + foo: string, + bar: TypeC +} + +interface TypeA { + foo: string, + bar: TypeB, +} + +let test: View; + +test = { foo: true, bar: true, boo: true } + +test = { foo: true, bar: { foo: true, bar: true, boo: true } } + + +//// [excessPropertyChecksWithNestedIntersections.js] +"use strict"; +// https://github.com/Microsoft/TypeScript/issues/13813 +exports.__esModule = true; +var a = { a: { x: 'hello' } }; // ok +var b = { a: { x: 2 } }; // error - types of property x are incompatible +var c = { a: { x: 'hello', y: 2 } }; // error - y does not exist in type A +var d = { a: { x: 'hello' }, c: 5 }; // ok +var e = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatible +var f = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error +exports.photo = { + id: 1, + url: '', + xyz: 1 // Great! This causes an error! +}; +exports.myInstance = { + id: 1, + name: '', + photo: { + id: 1, + url: '', + xyz: 2 // This should also be an error + } +}; +var test; +test = { foo: true, bar: true, boo: true }; +test = { foo: true, bar: { foo: true, bar: true, boo: true } }; diff --git a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.symbols b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.symbols new file mode 100644 index 00000000000..bf10195045e --- /dev/null +++ b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.symbols @@ -0,0 +1,190 @@ +=== tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts === +// https://github.com/Microsoft/TypeScript/issues/13813 + +interface A { +>A : Symbol(A, Decl(excessPropertyChecksWithNestedIntersections.ts, 0, 0)) + + x: string +>x : Symbol(A.x, Decl(excessPropertyChecksWithNestedIntersections.ts, 2, 13)) +} + +interface B { +>B : Symbol(B, Decl(excessPropertyChecksWithNestedIntersections.ts, 4, 1)) + + a: A; +>a : Symbol(B.a, Decl(excessPropertyChecksWithNestedIntersections.ts, 6, 13)) +>A : Symbol(A, Decl(excessPropertyChecksWithNestedIntersections.ts, 0, 0)) +} + +interface C { +>C : Symbol(C, Decl(excessPropertyChecksWithNestedIntersections.ts, 8, 1)) + + c: number; +>c : Symbol(C.c, Decl(excessPropertyChecksWithNestedIntersections.ts, 10, 13)) +} + +type D = B & C; +>D : Symbol(D, Decl(excessPropertyChecksWithNestedIntersections.ts, 12, 1)) +>B : Symbol(B, Decl(excessPropertyChecksWithNestedIntersections.ts, 4, 1)) +>C : Symbol(C, Decl(excessPropertyChecksWithNestedIntersections.ts, 8, 1)) + +let a: B = { a: { x: 'hello' } }; // ok +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 16, 3)) +>B : Symbol(B, Decl(excessPropertyChecksWithNestedIntersections.ts, 4, 1)) +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 16, 12)) +>x : Symbol(x, Decl(excessPropertyChecksWithNestedIntersections.ts, 16, 17)) + +let b: B = { a: { x: 2 } }; // error - types of property x are incompatible +>b : Symbol(b, Decl(excessPropertyChecksWithNestedIntersections.ts, 17, 3)) +>B : Symbol(B, Decl(excessPropertyChecksWithNestedIntersections.ts, 4, 1)) +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 17, 12)) +>x : Symbol(x, Decl(excessPropertyChecksWithNestedIntersections.ts, 17, 17)) + +let c: B = { a: { x: 'hello', y: 2 } }; // error - y does not exist in type A +>c : Symbol(c, Decl(excessPropertyChecksWithNestedIntersections.ts, 18, 3)) +>B : Symbol(B, Decl(excessPropertyChecksWithNestedIntersections.ts, 4, 1)) +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 18, 12)) +>x : Symbol(x, Decl(excessPropertyChecksWithNestedIntersections.ts, 18, 17)) +>y : Symbol(y, Decl(excessPropertyChecksWithNestedIntersections.ts, 18, 29)) + +let d: D = { a: { x: 'hello' }, c: 5 }; // ok +>d : Symbol(d, Decl(excessPropertyChecksWithNestedIntersections.ts, 20, 3)) +>D : Symbol(D, Decl(excessPropertyChecksWithNestedIntersections.ts, 12, 1)) +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 20, 12)) +>x : Symbol(x, Decl(excessPropertyChecksWithNestedIntersections.ts, 20, 17)) +>c : Symbol(c, Decl(excessPropertyChecksWithNestedIntersections.ts, 20, 31)) + +let e: D = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatible +>e : Symbol(e, Decl(excessPropertyChecksWithNestedIntersections.ts, 21, 3)) +>D : Symbol(D, Decl(excessPropertyChecksWithNestedIntersections.ts, 12, 1)) +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 21, 12)) +>x : Symbol(x, Decl(excessPropertyChecksWithNestedIntersections.ts, 21, 17)) +>c : Symbol(c, Decl(excessPropertyChecksWithNestedIntersections.ts, 21, 25)) + +let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error +>f : Symbol(f, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 3)) +>D : Symbol(D, Decl(excessPropertyChecksWithNestedIntersections.ts, 12, 1)) +>a : Symbol(a, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 12)) +>x : Symbol(x, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 17)) +>y : Symbol(y, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 29)) +>c : Symbol(c, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 37)) + +// https://github.com/Microsoft/TypeScript/issues/18075 + +export type MyType = { id: number; } & { name: string; } & { photo: { id: number; } & { url: string; } } +>MyType : Symbol(MyType, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 45)) +>id : Symbol(id, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 22)) +>name : Symbol(name, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 40)) +>photo : Symbol(photo, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 60)) +>id : Symbol(id, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 69)) +>url : Symbol(url, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 87)) + +export let obj: MyType; +>obj : Symbol(obj, Decl(excessPropertyChecksWithNestedIntersections.ts, 28, 10)) +>MyType : Symbol(MyType, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 45)) + +export const photo: typeof obj.photo = { +>photo : Symbol(photo, Decl(excessPropertyChecksWithNestedIntersections.ts, 30, 12)) +>obj.photo : Symbol(photo, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 60)) +>obj : Symbol(obj, Decl(excessPropertyChecksWithNestedIntersections.ts, 28, 10)) +>photo : Symbol(photo, Decl(excessPropertyChecksWithNestedIntersections.ts, 26, 60)) + + id: 1, +>id : Symbol(id, Decl(excessPropertyChecksWithNestedIntersections.ts, 30, 40)) + + url: '', +>url : Symbol(url, Decl(excessPropertyChecksWithNestedIntersections.ts, 31, 10)) + + xyz: 1 // Great! This causes an error! +>xyz : Symbol(xyz, Decl(excessPropertyChecksWithNestedIntersections.ts, 32, 12)) + +}; + +export const myInstance: MyType = { +>myInstance : Symbol(myInstance, Decl(excessPropertyChecksWithNestedIntersections.ts, 36, 12)) +>MyType : Symbol(MyType, Decl(excessPropertyChecksWithNestedIntersections.ts, 22, 45)) + + id: 1, +>id : Symbol(id, Decl(excessPropertyChecksWithNestedIntersections.ts, 36, 35)) + + name: '', +>name : Symbol(name, Decl(excessPropertyChecksWithNestedIntersections.ts, 37, 10)) + + photo: { +>photo : Symbol(photo, Decl(excessPropertyChecksWithNestedIntersections.ts, 38, 13)) + + id: 1, +>id : Symbol(id, Decl(excessPropertyChecksWithNestedIntersections.ts, 39, 12)) + + url: '', +>url : Symbol(url, Decl(excessPropertyChecksWithNestedIntersections.ts, 40, 14)) + + xyz: 2 // This should also be an error +>xyz : Symbol(xyz, Decl(excessPropertyChecksWithNestedIntersections.ts, 41, 16)) + } +}; + +// https://github.com/Microsoft/TypeScript/issues/28616 + +export type View = { [K in keyof T]: T[K] extends object ? boolean | View : boolean }; +>View : Symbol(View, Decl(excessPropertyChecksWithNestedIntersections.ts, 44, 2)) +>T : Symbol(T, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 17)) +>K : Symbol(K, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 25)) +>T : Symbol(T, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 17)) +>T : Symbol(T, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 17)) +>K : Symbol(K, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 25)) +>View : Symbol(View, Decl(excessPropertyChecksWithNestedIntersections.ts, 44, 2)) +>T : Symbol(T, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 17)) +>K : Symbol(K, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 25)) + +interface TypeC { +>TypeC : Symbol(TypeC, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 95)) + + foo: string; +>foo : Symbol(TypeC.foo, Decl(excessPropertyChecksWithNestedIntersections.ts, 50, 17)) + + bar: string; +>bar : Symbol(TypeC.bar, Decl(excessPropertyChecksWithNestedIntersections.ts, 51, 16)) +} + +interface TypeB { +>TypeB : Symbol(TypeB, Decl(excessPropertyChecksWithNestedIntersections.ts, 53, 1)) + + foo: string, +>foo : Symbol(TypeB.foo, Decl(excessPropertyChecksWithNestedIntersections.ts, 55, 17)) + + bar: TypeC +>bar : Symbol(TypeB.bar, Decl(excessPropertyChecksWithNestedIntersections.ts, 56, 16)) +>TypeC : Symbol(TypeC, Decl(excessPropertyChecksWithNestedIntersections.ts, 48, 95)) +} + +interface TypeA { +>TypeA : Symbol(TypeA, Decl(excessPropertyChecksWithNestedIntersections.ts, 58, 1)) + + foo: string, +>foo : Symbol(TypeA.foo, Decl(excessPropertyChecksWithNestedIntersections.ts, 60, 17)) + + bar: TypeB, +>bar : Symbol(TypeA.bar, Decl(excessPropertyChecksWithNestedIntersections.ts, 61, 16)) +>TypeB : Symbol(TypeB, Decl(excessPropertyChecksWithNestedIntersections.ts, 53, 1)) +} + +let test: View; +>test : Symbol(test, Decl(excessPropertyChecksWithNestedIntersections.ts, 65, 3)) +>View : Symbol(View, Decl(excessPropertyChecksWithNestedIntersections.ts, 44, 2)) +>TypeA : Symbol(TypeA, Decl(excessPropertyChecksWithNestedIntersections.ts, 58, 1)) + +test = { foo: true, bar: true, boo: true } +>test : Symbol(test, Decl(excessPropertyChecksWithNestedIntersections.ts, 65, 3)) +>foo : Symbol(foo, Decl(excessPropertyChecksWithNestedIntersections.ts, 67, 8)) +>bar : Symbol(bar, Decl(excessPropertyChecksWithNestedIntersections.ts, 67, 19)) +>boo : Symbol(boo, Decl(excessPropertyChecksWithNestedIntersections.ts, 67, 30)) + +test = { foo: true, bar: { foo: true, bar: true, boo: true } } +>test : Symbol(test, Decl(excessPropertyChecksWithNestedIntersections.ts, 65, 3)) +>foo : Symbol(foo, Decl(excessPropertyChecksWithNestedIntersections.ts, 69, 8)) +>bar : Symbol(bar, Decl(excessPropertyChecksWithNestedIntersections.ts, 69, 19)) +>foo : Symbol(foo, Decl(excessPropertyChecksWithNestedIntersections.ts, 69, 26)) +>bar : Symbol(bar, Decl(excessPropertyChecksWithNestedIntersections.ts, 69, 37)) +>boo : Symbol(boo, Decl(excessPropertyChecksWithNestedIntersections.ts, 69, 48)) + diff --git a/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.types b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.types new file mode 100644 index 00000000000..d15b8f7e0dd --- /dev/null +++ b/tests/baselines/reference/excessPropertyChecksWithNestedIntersections.types @@ -0,0 +1,201 @@ +=== tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts === +// https://github.com/Microsoft/TypeScript/issues/13813 + +interface A { + x: string +>x : string +} + +interface B { + a: A; +>a : A +} + +interface C { + c: number; +>c : number +} + +type D = B & C; +>D : D + +let a: B = { a: { x: 'hello' } }; // ok +>a : B +>{ a: { x: 'hello' } } : { a: { x: string; }; } +>a : { x: string; } +>{ x: 'hello' } : { x: string; } +>x : string +>'hello' : "hello" + +let b: B = { a: { x: 2 } }; // error - types of property x are incompatible +>b : B +>{ a: { x: 2 } } : { a: { x: number; }; } +>a : { x: number; } +>{ x: 2 } : { x: number; } +>x : number +>2 : 2 + +let c: B = { a: { x: 'hello', y: 2 } }; // error - y does not exist in type A +>c : B +>{ a: { x: 'hello', y: 2 } } : { a: { x: string; y: number; }; } +>a : { x: string; y: number; } +>{ x: 'hello', y: 2 } : { x: string; y: number; } +>x : string +>'hello' : "hello" +>y : number +>2 : 2 + +let d: D = { a: { x: 'hello' }, c: 5 }; // ok +>d : D +>{ a: { x: 'hello' }, c: 5 } : { a: { x: string; }; c: number; } +>a : { x: string; } +>{ x: 'hello' } : { x: string; } +>x : string +>'hello' : "hello" +>c : number +>5 : 5 + +let e: D = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatible +>e : D +>{ a: { x: 2 }, c: 5 } : { a: { x: number; }; c: number; } +>a : { x: number; } +>{ x: 2 } : { x: number; } +>x : number +>2 : 2 +>c : number +>5 : 5 + +let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error +>f : D +>{ a: { x: 'hello', y: 2 }, c: 5 } : { a: { x: string; y: number; }; c: number; } +>a : { x: string; y: number; } +>{ x: 'hello', y: 2 } : { x: string; y: number; } +>x : string +>'hello' : "hello" +>y : number +>2 : 2 +>c : number +>5 : 5 + +// https://github.com/Microsoft/TypeScript/issues/18075 + +export type MyType = { id: number; } & { name: string; } & { photo: { id: number; } & { url: string; } } +>MyType : MyType +>id : number +>name : string +>photo : { id: number; } & { url: string; } +>id : number +>url : string + +export let obj: MyType; +>obj : MyType + +export const photo: typeof obj.photo = { +>photo : { id: number; } & { url: string; } +>obj.photo : { id: number; } & { url: string; } +>obj : MyType +>photo : { id: number; } & { url: string; } +>{ id: 1, url: '', xyz: 1 // Great! This causes an error!} : { id: number; url: string; xyz: number; } + + id: 1, +>id : number +>1 : 1 + + url: '', +>url : string +>'' : "" + + xyz: 1 // Great! This causes an error! +>xyz : number +>1 : 1 + +}; + +export const myInstance: MyType = { +>myInstance : MyType +>{ id: 1, name: '', photo: { id: 1, url: '', xyz: 2 // This should also be an error }} : { id: number; name: string; photo: { id: number; url: string; xyz: number; }; } + + id: 1, +>id : number +>1 : 1 + + name: '', +>name : string +>'' : "" + + photo: { +>photo : { id: number; url: string; xyz: number; } +>{ id: 1, url: '', xyz: 2 // This should also be an error } : { id: number; url: string; xyz: number; } + + id: 1, +>id : number +>1 : 1 + + url: '', +>url : string +>'' : "" + + xyz: 2 // This should also be an error +>xyz : number +>2 : 2 + } +}; + +// https://github.com/Microsoft/TypeScript/issues/28616 + +export type View = { [K in keyof T]: T[K] extends object ? boolean | View : boolean }; +>View : View + +interface TypeC { + foo: string; +>foo : string + + bar: string; +>bar : string +} + +interface TypeB { + foo: string, +>foo : string + + bar: TypeC +>bar : TypeC +} + +interface TypeA { + foo: string, +>foo : string + + bar: TypeB, +>bar : TypeB +} + +let test: View; +>test : View + +test = { foo: true, bar: true, boo: true } +>test = { foo: true, bar: true, boo: true } : { foo: true; bar: true; boo: boolean; } +>test : View +>{ foo: true, bar: true, boo: true } : { foo: true; bar: true; boo: boolean; } +>foo : true +>true : true +>bar : true +>true : true +>boo : boolean +>true : true + +test = { foo: true, bar: { foo: true, bar: true, boo: true } } +>test = { foo: true, bar: { foo: true, bar: true, boo: true } } : { foo: true; bar: { foo: true; bar: true; boo: boolean; }; } +>test : View +>{ foo: true, bar: { foo: true, bar: true, boo: true } } : { foo: true; bar: { foo: true; bar: true; boo: boolean; }; } +>foo : true +>true : true +>bar : { foo: true; bar: true; boo: boolean; } +>{ foo: true, bar: true, boo: true } : { foo: true; bar: true; boo: boolean; } +>foo : true +>true : true +>bar : true +>true : true +>boo : boolean +>true : true + diff --git a/tests/baselines/reference/mappedToToIndexSignatureInference.js b/tests/baselines/reference/mappedToToIndexSignatureInference.js index 2ea09b663a9..03a24ab0ad9 100644 --- a/tests/baselines/reference/mappedToToIndexSignatureInference.js +++ b/tests/baselines/reference/mappedToToIndexSignatureInference.js @@ -2,7 +2,21 @@ declare const fn: (object: { [Key in K]: V }) => object; declare const a: { [index: string]: number }; fn(a); + +// Repro from #30218 + +declare function enumValues(e: Record): V[]; + +enum E { A = 'foo', B = 'bar' } + +let x: E[] = enumValues(E); //// [mappedToToIndexSignatureInference.js] fn(a); +var E; +(function (E) { + E["A"] = "foo"; + E["B"] = "bar"; +})(E || (E = {})); +var x = enumValues(E); diff --git a/tests/baselines/reference/mappedToToIndexSignatureInference.symbols b/tests/baselines/reference/mappedToToIndexSignatureInference.symbols index d071f3f4c1c..d2d6f923f87 100644 --- a/tests/baselines/reference/mappedToToIndexSignatureInference.symbols +++ b/tests/baselines/reference/mappedToToIndexSignatureInference.symbols @@ -16,3 +16,26 @@ fn(a); >fn : Symbol(fn, Decl(mappedToToIndexSignatureInference.ts, 0, 13)) >a : Symbol(a, Decl(mappedToToIndexSignatureInference.ts, 1, 13)) +// Repro from #30218 + +declare function enumValues(e: Record): V[]; +>enumValues : Symbol(enumValues, Decl(mappedToToIndexSignatureInference.ts, 2, 6)) +>K : Symbol(K, Decl(mappedToToIndexSignatureInference.ts, 6, 28)) +>V : Symbol(V, Decl(mappedToToIndexSignatureInference.ts, 6, 45)) +>e : Symbol(e, Decl(mappedToToIndexSignatureInference.ts, 6, 64)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>K : Symbol(K, Decl(mappedToToIndexSignatureInference.ts, 6, 28)) +>V : Symbol(V, Decl(mappedToToIndexSignatureInference.ts, 6, 45)) +>V : Symbol(V, Decl(mappedToToIndexSignatureInference.ts, 6, 45)) + +enum E { A = 'foo', B = 'bar' } +>E : Symbol(E, Decl(mappedToToIndexSignatureInference.ts, 6, 86)) +>A : Symbol(E.A, Decl(mappedToToIndexSignatureInference.ts, 8, 8)) +>B : Symbol(E.B, Decl(mappedToToIndexSignatureInference.ts, 8, 19)) + +let x: E[] = enumValues(E); +>x : Symbol(x, Decl(mappedToToIndexSignatureInference.ts, 10, 3)) +>E : Symbol(E, Decl(mappedToToIndexSignatureInference.ts, 6, 86)) +>enumValues : Symbol(enumValues, Decl(mappedToToIndexSignatureInference.ts, 2, 6)) +>E : Symbol(E, Decl(mappedToToIndexSignatureInference.ts, 6, 86)) + diff --git a/tests/baselines/reference/mappedToToIndexSignatureInference.types b/tests/baselines/reference/mappedToToIndexSignatureInference.types index 35b9b3565e0..21ab79e8e48 100644 --- a/tests/baselines/reference/mappedToToIndexSignatureInference.types +++ b/tests/baselines/reference/mappedToToIndexSignatureInference.types @@ -12,3 +12,22 @@ fn(a); >fn : (object: { [Key in K]: V; }) => object >a : { [index: string]: number; } +// Repro from #30218 + +declare function enumValues(e: Record): V[]; +>enumValues : (e: Record) => V[] +>e : Record + +enum E { A = 'foo', B = 'bar' } +>E : E +>A : E.A +>'foo' : "foo" +>B : E.B +>'bar' : "bar" + +let x: E[] = enumValues(E); +>x : E[] +>enumValues(E) : E[] +>enumValues : (e: Record) => V[] +>E : typeof E + diff --git a/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.errors.txt b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.errors.txt new file mode 100644 index 00000000000..4cf8a99e0ea --- /dev/null +++ b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.errors.txt @@ -0,0 +1,44 @@ +tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts(13,35): error TS2322: Type '{ INVALID_PROP_NAME: string; iconProp: string; }' is not assignable to type 'number | IProps'. + Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'IProps'. +tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts(16,7): error TS2322: Type '{ nestedProp: { asdfasdf: string; }; iconProp: string; }' is not assignable to type 'number | IProps'. + Type '{ nestedProp: { asdfasdf: string; }; iconProp: string; }' is not assignable to type 'IProps'. + Types of property 'nestedProp' are incompatible. + Type '{ asdfasdf: string; }' has no properties in common with type '{ testBool?: boolean; }'. +tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts(19,56): error TS2326: Types of property 'nestedProps' are incompatible. + Type '{ INVALID_PROP_NAME: string; iconProp: string; }' is not assignable to type 'IProps'. + Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'IProps'. + + +==== tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts (3 errors) ==== + interface IProps { + iconProp?: string; + nestedProp?: { + testBool?: boolean; + } + } + + interface INestedProps { + nestedProps?: IProps; + } + + // These are the types of errors we want: + const propB1: IProps | number = { INVALID_PROP_NAME: 'share', iconProp: 'test' }; + ~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ INVALID_PROP_NAME: string; iconProp: string; }' is not assignable to type 'number | IProps'. +!!! error TS2322: Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'IProps'. + + // Nested typing works here and we also get an expected error: + const propB2: IProps | number = { nestedProp: { asdfasdf: 'test' }, iconProp: 'test' }; + ~~~~~~ +!!! error TS2322: Type '{ nestedProp: { asdfasdf: string; }; iconProp: string; }' is not assignable to type 'number | IProps'. +!!! error TS2322: Type '{ nestedProp: { asdfasdf: string; }; iconProp: string; }' is not assignable to type 'IProps'. +!!! error TS2322: Types of property 'nestedProp' are incompatible. +!!! error TS2322: Type '{ asdfasdf: string; }' has no properties in common with type '{ testBool?: boolean; }'. + + // Want an error generated here but there isn't one. + const propA1: INestedProps | number = { nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } }; + ~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2326: Types of property 'nestedProps' are incompatible. +!!! error TS2326: Type '{ INVALID_PROP_NAME: string; iconProp: string; }' is not assignable to type 'IProps'. +!!! error TS2326: Object literal may only specify known properties, and 'INVALID_PROP_NAME' does not exist in type 'IProps'. + \ No newline at end of file diff --git a/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.js b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.js new file mode 100644 index 00000000000..b286049e4a4 --- /dev/null +++ b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.js @@ -0,0 +1,29 @@ +//// [nonObjectUnionNestedExcessPropertyCheck.ts] +interface IProps { + iconProp?: string; + nestedProp?: { + testBool?: boolean; + } +} + +interface INestedProps { + nestedProps?: IProps; +} + +// These are the types of errors we want: +const propB1: IProps | number = { INVALID_PROP_NAME: 'share', iconProp: 'test' }; + +// Nested typing works here and we also get an expected error: +const propB2: IProps | number = { nestedProp: { asdfasdf: 'test' }, iconProp: 'test' }; + +// Want an error generated here but there isn't one. +const propA1: INestedProps | number = { nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } }; + + +//// [nonObjectUnionNestedExcessPropertyCheck.js] +// These are the types of errors we want: +var propB1 = { INVALID_PROP_NAME: 'share', iconProp: 'test' }; +// Nested typing works here and we also get an expected error: +var propB2 = { nestedProp: { asdfasdf: 'test' }, iconProp: 'test' }; +// Want an error generated here but there isn't one. +var propA1 = { nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } }; diff --git a/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.symbols b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.symbols new file mode 100644 index 00000000000..95e6c33a64e --- /dev/null +++ b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.symbols @@ -0,0 +1,46 @@ +=== tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts === +interface IProps { +>IProps : Symbol(IProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 0, 0)) + + iconProp?: string; +>iconProp : Symbol(IProps.iconProp, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 0, 18)) + + nestedProp?: { +>nestedProp : Symbol(IProps.nestedProp, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 1, 22)) + + testBool?: boolean; +>testBool : Symbol(testBool, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 2, 18)) + } +} + +interface INestedProps { +>INestedProps : Symbol(INestedProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 5, 1)) + + nestedProps?: IProps; +>nestedProps : Symbol(INestedProps.nestedProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 7, 24)) +>IProps : Symbol(IProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 0, 0)) +} + +// These are the types of errors we want: +const propB1: IProps | number = { INVALID_PROP_NAME: 'share', iconProp: 'test' }; +>propB1 : Symbol(propB1, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 12, 5)) +>IProps : Symbol(IProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 0, 0)) +>INVALID_PROP_NAME : Symbol(INVALID_PROP_NAME, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 12, 33)) +>iconProp : Symbol(iconProp, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 12, 61)) + +// Nested typing works here and we also get an expected error: +const propB2: IProps | number = { nestedProp: { asdfasdf: 'test' }, iconProp: 'test' }; +>propB2 : Symbol(propB2, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 15, 5)) +>IProps : Symbol(IProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 0, 0)) +>nestedProp : Symbol(nestedProp, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 15, 33)) +>asdfasdf : Symbol(asdfasdf, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 15, 47)) +>iconProp : Symbol(iconProp, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 15, 67)) + +// Want an error generated here but there isn't one. +const propA1: INestedProps | number = { nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } }; +>propA1 : Symbol(propA1, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 18, 5)) +>INestedProps : Symbol(INestedProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 5, 1)) +>nestedProps : Symbol(nestedProps, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 18, 39)) +>INVALID_PROP_NAME : Symbol(INVALID_PROP_NAME, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 18, 54)) +>iconProp : Symbol(iconProp, Decl(nonObjectUnionNestedExcessPropertyCheck.ts, 18, 82)) + diff --git a/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.types b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.types new file mode 100644 index 00000000000..3e05dbd3628 --- /dev/null +++ b/tests/baselines/reference/nonObjectUnionNestedExcessPropertyCheck.types @@ -0,0 +1,49 @@ +=== tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts === +interface IProps { + iconProp?: string; +>iconProp : string + + nestedProp?: { +>nestedProp : { testBool?: boolean; } + + testBool?: boolean; +>testBool : boolean + } +} + +interface INestedProps { + nestedProps?: IProps; +>nestedProps : IProps +} + +// These are the types of errors we want: +const propB1: IProps | number = { INVALID_PROP_NAME: 'share', iconProp: 'test' }; +>propB1 : number | IProps +>{ INVALID_PROP_NAME: 'share', iconProp: 'test' } : { INVALID_PROP_NAME: string; iconProp: string; } +>INVALID_PROP_NAME : string +>'share' : "share" +>iconProp : string +>'test' : "test" + +// Nested typing works here and we also get an expected error: +const propB2: IProps | number = { nestedProp: { asdfasdf: 'test' }, iconProp: 'test' }; +>propB2 : number | IProps +>{ nestedProp: { asdfasdf: 'test' }, iconProp: 'test' } : { nestedProp: { asdfasdf: string; }; iconProp: string; } +>nestedProp : { asdfasdf: string; } +>{ asdfasdf: 'test' } : { asdfasdf: string; } +>asdfasdf : string +>'test' : "test" +>iconProp : string +>'test' : "test" + +// Want an error generated here but there isn't one. +const propA1: INestedProps | number = { nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } }; +>propA1 : number | INestedProps +>{ nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } } : { nestedProps: { INVALID_PROP_NAME: string; iconProp: string; }; } +>nestedProps : { INVALID_PROP_NAME: string; iconProp: string; } +>{ INVALID_PROP_NAME: 'share', iconProp: 'test' } : { INVALID_PROP_NAME: string; iconProp: string; } +>INVALID_PROP_NAME : string +>'share' : "share" +>iconProp : string +>'test' : "test" + diff --git a/tests/baselines/reference/tsbuild/sample1/incremental-declaration-changes/when-declaration-option-changes.js b/tests/baselines/reference/tsbuild/sample1/incremental-declaration-changes/when-declaration-option-changes.js new file mode 100644 index 00000000000..ad16296e10c --- /dev/null +++ b/tests/baselines/reference/tsbuild/sample1/incremental-declaration-changes/when-declaration-option-changes.js @@ -0,0 +1,77 @@ +//// [/src/core/anotherModule.d.ts] +export declare const World = "hello"; + + +//// [/src/core/index.d.ts] +export declare const someString: string; +export declare function leftPad(s: string, n: number): string; +export declare function multiply(a: number, b: number): number; + + +//// [/src/core/tsconfig.json] +{ + "compilerOptions": { + "incremental": true, "declaration": true, + "skipDefaultLibCheck": true + } +} + +//// [/src/core/tsconfig.tsbuildinfo] +{ + "program": { + "fileInfos": { + "/lib/lib.d.ts": { + "version": "/lib/lib.d.ts", + "signature": "/lib/lib.d.ts" + }, + "/lib/lib.es5.d.ts": { + "version": "/lib/lib.es5.d.ts", + "signature": "/lib/lib.es5.d.ts" + }, + "/lib/lib.dom.d.ts": { + "version": "/lib/lib.dom.d.ts", + "signature": "/lib/lib.dom.d.ts" + }, + "/lib/lib.webworker.importscripts.d.ts": { + "version": "/lib/lib.webworker.importscripts.d.ts", + "signature": "/lib/lib.webworker.importscripts.d.ts" + }, + "/lib/lib.scripthost.d.ts": { + "version": "/lib/lib.scripthost.d.ts", + "signature": "/lib/lib.scripthost.d.ts" + }, + "/src/core/anothermodule.ts": { + "version": "-2676574883", + "signature": "-8396256275" + }, + "/src/core/index.ts": { + "version": "-18749805970", + "signature": "1874987148" + }, + "/src/core/some_decl.d.ts": { + "version": "-9253692965", + "signature": "-9253692965" + } + }, + "options": { + "incremental": true, + "declaration": true, + "skipDefaultLibCheck": true, + "configFilePath": "/src/core/tsconfig.json" + }, + "referencedMap": {}, + "exportedModulesMap": {}, + "semanticDiagnosticsPerFile": [ + "/lib/lib.d.ts", + "/lib/lib.dom.d.ts", + "/lib/lib.es5.d.ts", + "/lib/lib.scripthost.d.ts", + "/lib/lib.webworker.importscripts.d.ts", + "/src/core/anothermodule.ts", + "/src/core/index.ts", + "/src/core/some_decl.d.ts" + ] + }, + "version": "FakeTSVersion" +} + diff --git a/tests/baselines/reference/tsbuild/sample1/initial-Build/when-declaration-option-changes.js b/tests/baselines/reference/tsbuild/sample1/initial-Build/when-declaration-option-changes.js new file mode 100644 index 00000000000..341b277de0d --- /dev/null +++ b/tests/baselines/reference/tsbuild/sample1/initial-Build/when-declaration-option-changes.js @@ -0,0 +1,82 @@ +//// [/src/core/anotherModule.js] +"use strict"; +exports.__esModule = true; +exports.World = "hello"; + + +//// [/src/core/index.js] +"use strict"; +exports.__esModule = true; +exports.someString = "HELLO WORLD"; +function leftPad(s, n) { return s + n; } +exports.leftPad = leftPad; +function multiply(a, b) { return a * b; } +exports.multiply = multiply; + + +//// [/src/core/tsconfig.json] +{ + "compilerOptions": { + "incremental": true, + "skipDefaultLibCheck": true + } +} + +//// [/src/core/tsconfig.tsbuildinfo] +{ + "program": { + "fileInfos": { + "/lib/lib.d.ts": { + "version": "/lib/lib.d.ts", + "signature": "/lib/lib.d.ts" + }, + "/lib/lib.es5.d.ts": { + "version": "/lib/lib.es5.d.ts", + "signature": "/lib/lib.es5.d.ts" + }, + "/lib/lib.dom.d.ts": { + "version": "/lib/lib.dom.d.ts", + "signature": "/lib/lib.dom.d.ts" + }, + "/lib/lib.webworker.importscripts.d.ts": { + "version": "/lib/lib.webworker.importscripts.d.ts", + "signature": "/lib/lib.webworker.importscripts.d.ts" + }, + "/lib/lib.scripthost.d.ts": { + "version": "/lib/lib.scripthost.d.ts", + "signature": "/lib/lib.scripthost.d.ts" + }, + "/src/core/anothermodule.ts": { + "version": "-2676574883", + "signature": "-8396256275" + }, + "/src/core/index.ts": { + "version": "-18749805970", + "signature": "1874987148" + }, + "/src/core/some_decl.d.ts": { + "version": "-9253692965", + "signature": "-9253692965" + } + }, + "options": { + "incremental": true, + "skipDefaultLibCheck": true, + "configFilePath": "/src/core/tsconfig.json" + }, + "referencedMap": {}, + "exportedModulesMap": {}, + "semanticDiagnosticsPerFile": [ + "/lib/lib.d.ts", + "/lib/lib.dom.d.ts", + "/lib/lib.es5.d.ts", + "/lib/lib.scripthost.d.ts", + "/lib/lib.webworker.importscripts.d.ts", + "/src/core/anothermodule.ts", + "/src/core/index.ts", + "/src/core/some_decl.d.ts" + ] + }, + "version": "FakeTSVersion" +} + diff --git a/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.js b/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.js new file mode 100644 index 00000000000..fa996071923 --- /dev/null +++ b/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.js @@ -0,0 +1,17 @@ +//// [unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts] +interface IStringDictionary { + [name: string]: V; +} +interface INumberDictionary { + [idx: number]: V; +} + +declare function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any); + +let count = 0; +forEach({ toString: 123 }, () => count++); + + +//// [unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.js] +var count = 0; +forEach({ toString: 123 }, function () { return count++; }); diff --git a/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.symbols b/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.symbols new file mode 100644 index 00000000000..f671260a7a3 --- /dev/null +++ b/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.symbols @@ -0,0 +1,41 @@ +=== tests/cases/compiler/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts === +interface IStringDictionary { +>IStringDictionary : Symbol(IStringDictionary, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 0, 0)) +>V : Symbol(V, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 0, 28)) + + [name: string]: V; +>name : Symbol(name, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 1, 2)) +>V : Symbol(V, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 0, 28)) +} +interface INumberDictionary { +>INumberDictionary : Symbol(INumberDictionary, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 2, 1)) +>V : Symbol(V, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 3, 28)) + + [idx: number]: V; +>idx : Symbol(idx, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 4, 2)) +>V : Symbol(V, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 3, 28)) +} + +declare function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any); +>forEach : Symbol(forEach, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 5, 1)) +>T : Symbol(T, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 25)) +>from : Symbol(from, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 28)) +>IStringDictionary : Symbol(IStringDictionary, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 0, 0)) +>T : Symbol(T, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 25)) +>INumberDictionary : Symbol(INumberDictionary, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 2, 1)) +>T : Symbol(T, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 25)) +>callback : Symbol(callback, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 78)) +>entry : Symbol(entry, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 90)) +>key : Symbol(key, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 98)) +>value : Symbol(value, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 108)) +>T : Symbol(T, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 25)) +>remove : Symbol(remove, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 7, 121)) + +let count = 0; +>count : Symbol(count, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 9, 3)) + +forEach({ toString: 123 }, () => count++); +>forEach : Symbol(forEach, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 5, 1)) +>toString : Symbol(toString, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 10, 9)) +>count : Symbol(count, Decl(unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts, 9, 3)) + diff --git a/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.types b/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.types new file mode 100644 index 00000000000..83bd83d6ec1 --- /dev/null +++ b/tests/baselines/reference/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.types @@ -0,0 +1,33 @@ +=== tests/cases/compiler/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts === +interface IStringDictionary { + [name: string]: V; +>name : string +} +interface INumberDictionary { + [idx: number]: V; +>idx : number +} + +declare function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any); +>forEach : (from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any) => any +>from : IStringDictionary | INumberDictionary +>callback : (entry: { key: any; value: T; }, remove: () => void) => any +>entry : { key: any; value: T; } +>key : any +>value : T +>remove : () => void + +let count = 0; +>count : number +>0 : 0 + +forEach({ toString: 123 }, () => count++); +>forEach({ toString: 123 }, () => count++) : any +>forEach : (from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any) => any +>{ toString: 123 } : { toString: number; } +>toString : number +>123 : 123 +>() => count++ : () => number +>count++ : number +>count : number + diff --git a/tests/baselines/reference/unionExcessPropsWithPartialMember.js b/tests/baselines/reference/unionExcessPropsWithPartialMember.js new file mode 100644 index 00000000000..cefd480bba8 --- /dev/null +++ b/tests/baselines/reference/unionExcessPropsWithPartialMember.js @@ -0,0 +1,31 @@ +//// [unionExcessPropsWithPartialMember.ts] +interface A { + unused?: string; + x: string; +} + +interface B { + x: string; + y: string; +} + +declare var ab: A | B; +declare var a: A; + +ab = {...a, y: (null as any as string | undefined)}; // Should be allowed, since `y` is missing on `A` + + +//// [unionExcessPropsWithPartialMember.js] +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +ab = __assign({}, a, { y: null }); // Should be allowed, since `y` is missing on `A` diff --git a/tests/baselines/reference/unionExcessPropsWithPartialMember.symbols b/tests/baselines/reference/unionExcessPropsWithPartialMember.symbols new file mode 100644 index 00000000000..a81636816a5 --- /dev/null +++ b/tests/baselines/reference/unionExcessPropsWithPartialMember.symbols @@ -0,0 +1,35 @@ +=== tests/cases/compiler/unionExcessPropsWithPartialMember.ts === +interface A { +>A : Symbol(A, Decl(unionExcessPropsWithPartialMember.ts, 0, 0)) + + unused?: string; +>unused : Symbol(A.unused, Decl(unionExcessPropsWithPartialMember.ts, 0, 13)) + + x: string; +>x : Symbol(A.x, Decl(unionExcessPropsWithPartialMember.ts, 1, 20)) +} + +interface B { +>B : Symbol(B, Decl(unionExcessPropsWithPartialMember.ts, 3, 1)) + + x: string; +>x : Symbol(B.x, Decl(unionExcessPropsWithPartialMember.ts, 5, 13)) + + y: string; +>y : Symbol(B.y, Decl(unionExcessPropsWithPartialMember.ts, 6, 14)) +} + +declare var ab: A | B; +>ab : Symbol(ab, Decl(unionExcessPropsWithPartialMember.ts, 10, 11)) +>A : Symbol(A, Decl(unionExcessPropsWithPartialMember.ts, 0, 0)) +>B : Symbol(B, Decl(unionExcessPropsWithPartialMember.ts, 3, 1)) + +declare var a: A; +>a : Symbol(a, Decl(unionExcessPropsWithPartialMember.ts, 11, 11)) +>A : Symbol(A, Decl(unionExcessPropsWithPartialMember.ts, 0, 0)) + +ab = {...a, y: (null as any as string | undefined)}; // Should be allowed, since `y` is missing on `A` +>ab : Symbol(ab, Decl(unionExcessPropsWithPartialMember.ts, 10, 11)) +>a : Symbol(a, Decl(unionExcessPropsWithPartialMember.ts, 11, 11)) +>y : Symbol(y, Decl(unionExcessPropsWithPartialMember.ts, 13, 11)) + diff --git a/tests/baselines/reference/unionExcessPropsWithPartialMember.types b/tests/baselines/reference/unionExcessPropsWithPartialMember.types new file mode 100644 index 00000000000..2c3460d977c --- /dev/null +++ b/tests/baselines/reference/unionExcessPropsWithPartialMember.types @@ -0,0 +1,34 @@ +=== tests/cases/compiler/unionExcessPropsWithPartialMember.ts === +interface A { + unused?: string; +>unused : string | undefined + + x: string; +>x : string +} + +interface B { + x: string; +>x : string + + y: string; +>y : string +} + +declare var ab: A | B; +>ab : A | B + +declare var a: A; +>a : A + +ab = {...a, y: (null as any as string | undefined)}; // Should be allowed, since `y` is missing on `A` +>ab = {...a, y: (null as any as string | undefined)} : { y: string | undefined; unused?: string | undefined; x: string; } +>ab : A | B +>{...a, y: (null as any as string | undefined)} : { y: string | undefined; unused?: string | undefined; x: string; } +>a : A +>y : string | undefined +>(null as any as string | undefined) : string | undefined +>null as any as string | undefined : string | undefined +>null as any : any +>null : null + diff --git a/tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts b/tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts new file mode 100644 index 00000000000..ba7c2cf29d9 --- /dev/null +++ b/tests/cases/compiler/deepExcessPropertyCheckingWhenTargetIsIntersection.ts @@ -0,0 +1,27 @@ +interface StatelessComponent

{ + (props: P & { children?: number }, context?: any): null; +} + +const TestComponent: StatelessComponent = (props) => { + return null; +} + +interface ITestProps { + ariaLabel?: string; +} + +interface NestedProp { + props: TProps; +} + +interface TestProps { + icon: NestedProp; +} + +TestComponent({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); + +const TestComponent2: StatelessComponent = (props) => { + return null; +} + +TestComponent2({icon: { props: { INVALID_PROP_NAME: 'share', ariaLabel: 'test label' } }}); diff --git a/tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts b/tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts new file mode 100644 index 00000000000..86bf8f6261c --- /dev/null +++ b/tests/cases/compiler/excessPropertyChecksWithNestedIntersections.ts @@ -0,0 +1,70 @@ +// https://github.com/Microsoft/TypeScript/issues/13813 + +interface A { + x: string +} + +interface B { + a: A; +} + +interface C { + c: number; +} + +type D = B & C; + +let a: B = { a: { x: 'hello' } }; // ok +let b: B = { a: { x: 2 } }; // error - types of property x are incompatible +let c: B = { a: { x: 'hello', y: 2 } }; // error - y does not exist in type A + +let d: D = { a: { x: 'hello' }, c: 5 }; // ok +let e: D = { a: { x: 2 }, c: 5 }; // error - types of property x are incompatible +let f: D = { a: { x: 'hello', y: 2 }, c: 5 }; // should be an error + +// https://github.com/Microsoft/TypeScript/issues/18075 + +export type MyType = { id: number; } & { name: string; } & { photo: { id: number; } & { url: string; } } + +export let obj: MyType; + +export const photo: typeof obj.photo = { + id: 1, + url: '', + xyz: 1 // Great! This causes an error! +}; + +export const myInstance: MyType = { + id: 1, + name: '', + photo: { + id: 1, + url: '', + xyz: 2 // This should also be an error + } +}; + +// https://github.com/Microsoft/TypeScript/issues/28616 + +export type View = { [K in keyof T]: T[K] extends object ? boolean | View : boolean }; + +interface TypeC { + foo: string; + bar: string; +} + +interface TypeB { + foo: string, + bar: TypeC +} + +interface TypeA { + foo: string, + bar: TypeB, +} + +let test: View; + +test = { foo: true, bar: true, boo: true } + +test = { foo: true, bar: { foo: true, bar: true, boo: true } } diff --git a/tests/cases/compiler/mappedToToIndexSignatureInference.ts b/tests/cases/compiler/mappedToToIndexSignatureInference.ts index 6ce63ead59c..ee6f71a0616 100644 --- a/tests/cases/compiler/mappedToToIndexSignatureInference.ts +++ b/tests/cases/compiler/mappedToToIndexSignatureInference.ts @@ -1,3 +1,11 @@ declare const fn: (object: { [Key in K]: V }) => object; declare const a: { [index: string]: number }; fn(a); + +// Repro from #30218 + +declare function enumValues(e: Record): V[]; + +enum E { A = 'foo', B = 'bar' } + +let x: E[] = enumValues(E); diff --git a/tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts b/tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts new file mode 100644 index 00000000000..5050d33c05e --- /dev/null +++ b/tests/cases/compiler/nonObjectUnionNestedExcessPropertyCheck.ts @@ -0,0 +1,19 @@ +interface IProps { + iconProp?: string; + nestedProp?: { + testBool?: boolean; + } +} + +interface INestedProps { + nestedProps?: IProps; +} + +// These are the types of errors we want: +const propB1: IProps | number = { INVALID_PROP_NAME: 'share', iconProp: 'test' }; + +// Nested typing works here and we also get an expected error: +const propB2: IProps | number = { nestedProp: { asdfasdf: 'test' }, iconProp: 'test' }; + +// Want an error generated here but there isn't one. +const propA1: INestedProps | number = { nestedProps: { INVALID_PROP_NAME: 'share', iconProp: 'test' } }; diff --git a/tests/cases/compiler/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts b/tests/cases/compiler/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts new file mode 100644 index 00000000000..757c0f9917b --- /dev/null +++ b/tests/cases/compiler/unionExcessPropertyCheckNoApparentPropTypeMismatchErrors.ts @@ -0,0 +1,11 @@ +interface IStringDictionary { + [name: string]: V; +} +interface INumberDictionary { + [idx: number]: V; +} + +declare function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any); + +let count = 0; +forEach({ toString: 123 }, () => count++); diff --git a/tests/cases/compiler/unionExcessPropsWithPartialMember.ts b/tests/cases/compiler/unionExcessPropsWithPartialMember.ts new file mode 100644 index 00000000000..05f0475e0d0 --- /dev/null +++ b/tests/cases/compiler/unionExcessPropsWithPartialMember.ts @@ -0,0 +1,15 @@ +// @strict: true +interface A { + unused?: string; + x: string; +} + +interface B { + x: string; + y: string; +} + +declare var ab: A | B; +declare var a: A; + +ab = {...a, y: (null as any as string | undefined)}; // Should be allowed, since `y` is missing on `A`