Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7798f69

Browse files
authored
Merge pull request#19513 from Microsoft/strictObjectLiterals
Improved type inference for object literals
2 parentsb5f292d +fd0d40c commit7798f69

File tree

40 files changed

+1196
-138
lines changed

40 files changed

+1196
-138
lines changed

‎src/compiler/checker.ts‎

Lines changed: 130 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ namespace ts {
260260
const literalTypes = createMap<LiteralType>();
261261
const indexedAccessTypes = createMap<IndexedAccessType>();
262262
const evolvingArrayTypes: EvolvingArrayType[] = [];
263+
const undefinedProperties = createMap<Symbol>() as UnderscoreEscapedMap<Symbol>;
263264

264265
const unknownSymbol = createSymbol(SymbolFlags.Property, "unknown" as __String);
265266
const resolvingSymbol = createSymbol(0, InternalSymbolName.Resolving);
@@ -7984,7 +7985,7 @@ namespace ts {
79847985
const spread = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
79857986
spread.flags |= propagatedFlags;
79867987
spread.flags |= TypeFlags.FreshLiteral | TypeFlags.ContainsObjectLiteral;
7987-
(spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral;
7988+
(spread as ObjectType).objectFlags |=(ObjectFlags.ObjectLiteral | ObjectFlags.ContainsSpread);
79887989
spread.symbol = symbol;
79897990
return spread;
79907991
}
@@ -9026,7 +9027,7 @@ namespace ts {
90269027

90279028
if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True;
90289029

9029-
if (getObjectFlags(source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) {
9030+
if (isObjectLiteralType(source) && source.flags & TypeFlags.FreshLiteral) {
90309031
if (hasExcessProperties(<FreshObjectLiteralType>source, target, reportErrors)) {
90319032
if (reportErrors) {
90329033
reportRelationError(headMessage, source, target);
@@ -9596,14 +9597,27 @@ namespace ts {
95969597
if (relation === identityRelation) {
95979598
return propertiesIdenticalTo(source, target);
95989599
}
9599-
const requireOptionalProperties = relation === subtypeRelation && !(getObjectFlags(source) & ObjectFlags.ObjectLiteral);
9600+
const requireOptionalProperties = relation === subtypeRelation && !isObjectLiteralType(source);
96009601
const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties);
96019602
if (unmatchedProperty) {
96029603
if (reportErrors) {
96039604
reportError(Diagnostics.Property_0_is_missing_in_type_1, symbolToString(unmatchedProperty), typeToString(source));
96049605
}
96059606
return Ternary.False;
96069607
}
9608+
if (isObjectLiteralType(target)) {
9609+
for (const sourceProp of getPropertiesOfType(source)) {
9610+
if (!getPropertyOfObjectType(target, sourceProp.escapedName)) {
9611+
const sourceType = getTypeOfSymbol(sourceProp);
9612+
if (!(sourceType === undefinedType || sourceType === undefinedWideningType)) {
9613+
if (reportErrors) {
9614+
reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(sourceProp), typeToString(target));
9615+
}
9616+
return Ternary.False;
9617+
}
9618+
}
9619+
}
9620+
}
96079621
let result = Ternary.True;
96089622
const properties = getPropertiesOfObjectType(target);
96099623
for (const targetProp of properties) {
@@ -10425,7 +10439,7 @@ namespace ts {
1042510439
* Leave signatures alone since they are not subject to the check.
1042610440
*/
1042710441
function getRegularTypeOfObjectLiteral(type: Type): Type {
10428-
if (!(getObjectFlags(type) & ObjectFlags.ObjectLiteral && type.flags & TypeFlags.FreshLiteral)) {
10442+
if (!(isObjectLiteralType(type) && type.flags & TypeFlags.FreshLiteral)) {
1042910443
return type;
1043010444
}
1043110445
const regularType = (<FreshObjectLiteralType>type).regularType;
@@ -10447,18 +10461,74 @@ namespace ts {
1044710461
return regularNew;
1044810462
}
1044910463

10450-
function getWidenedProperty(prop: Symbol): Symbol {
10464+
function createWideningContext(parent: WideningContext, propertyName: __String, siblings: Type[]): WideningContext {
10465+
return { parent, propertyName, siblings, resolvedPropertyNames: undefined };
10466+
}
10467+
10468+
function getSiblingsOfContext(context: WideningContext): Type[] {
10469+
if (!context.siblings) {
10470+
const siblings: Type[] = [];
10471+
for (const type of getSiblingsOfContext(context.parent)) {
10472+
if (isObjectLiteralType(type)) {
10473+
const prop = getPropertyOfObjectType(type, context.propertyName);
10474+
if (prop) {
10475+
forEachType(getTypeOfSymbol(prop), t => {
10476+
siblings.push(t);
10477+
});
10478+
}
10479+
}
10480+
}
10481+
context.siblings = siblings;
10482+
}
10483+
return context.siblings;
10484+
}
10485+
10486+
function getPropertyNamesOfContext(context: WideningContext): __String[] {
10487+
if (!context.resolvedPropertyNames) {
10488+
const names = createMap<boolean>() as UnderscoreEscapedMap<boolean>;
10489+
for (const t of getSiblingsOfContext(context)) {
10490+
if (isObjectLiteralType(t) && !(getObjectFlags(t) & ObjectFlags.ContainsSpread)) {
10491+
for (const prop of getPropertiesOfType(t)) {
10492+
names.set(prop.escapedName, true);
10493+
}
10494+
}
10495+
}
10496+
context.resolvedPropertyNames = arrayFrom(names.keys());
10497+
}
10498+
return context.resolvedPropertyNames;
10499+
}
10500+
10501+
function getWidenedProperty(prop: Symbol, context: WideningContext): Symbol {
1045110502
const original = getTypeOfSymbol(prop);
10452-
const widened = getWidenedType(original);
10503+
const propContext = context && createWideningContext(context, prop.escapedName, /*siblings*/ undefined);
10504+
const widened = getWidenedTypeWithContext(original, propContext);
1045310505
return widened === original ? prop : createSymbolWithType(prop, widened);
1045410506
}
1045510507

10456-
function getWidenedTypeOfObjectLiteral(type: Type): Type {
10508+
function getUndefinedProperty(name: __String) {
10509+
const cached = undefinedProperties.get(name);
10510+
if (cached) {
10511+
return cached;
10512+
}
10513+
const result = createSymbol(SymbolFlags.Property | SymbolFlags.Optional, name);
10514+
result.type = undefinedType;
10515+
undefinedProperties.set(name, result);
10516+
return result;
10517+
}
10518+
10519+
function getWidenedTypeOfObjectLiteral(type: Type, context: WideningContext): Type {
1045710520
const members = createSymbolTable();
1045810521
for (const prop of getPropertiesOfObjectType(type)) {
1045910522
// Since get accessors already widen their return value there is no need to
1046010523
// widen accessor based properties here.
10461-
members.set(prop.escapedName, prop.flags & SymbolFlags.Property ? getWidenedProperty(prop) : prop);
10524+
members.set(prop.escapedName, prop.flags & SymbolFlags.Property ? getWidenedProperty(prop, context) : prop);
10525+
}
10526+
if (context) {
10527+
for (const name of getPropertyNamesOfContext(context)) {
10528+
if (!members.has(name)) {
10529+
members.set(name, getUndefinedProperty(name));
10530+
}
10531+
}
1046210532
}
1046310533
const stringIndexInfo = getIndexInfoOfType(type, IndexKind.String);
1046410534
const numberIndexInfo = getIndexInfoOfType(type, IndexKind.Number);
@@ -10467,20 +10537,25 @@ namespace ts {
1046710537
numberIndexInfo && createIndexInfo(getWidenedType(numberIndexInfo.type), numberIndexInfo.isReadonly));
1046810538
}
1046910539

10470-
functiongetWidenedConstituentType(type: Type): Type {
10471-
return type.flags & TypeFlags.Nullable ? type : getWidenedType(type);
10540+
functiongetWidenedType(type: Type) {
10541+
returngetWidenedTypeWithContext(type, /*context*/ undefined);
1047210542
}
1047310543

10474-
functiongetWidenedType(type: Type): Type {
10544+
functiongetWidenedTypeWithContext(type: Type, context: WideningContext): Type {
1047510545
if (type.flags & TypeFlags.RequiresWidening) {
1047610546
if (type.flags & TypeFlags.Nullable) {
1047710547
return anyType;
1047810548
}
10479-
if (getObjectFlags(type) & ObjectFlags.ObjectLiteral) {
10480-
return getWidenedTypeOfObjectLiteral(type);
10549+
if (isObjectLiteralType(type)) {
10550+
return getWidenedTypeOfObjectLiteral(type, context);
1048110551
}
1048210552
if (type.flags & TypeFlags.Union) {
10483-
return getUnionType(sameMap((<UnionType>type).types, getWidenedConstituentType));
10553+
const unionContext = context || createWideningContext(/*parent*/ undefined, /*propertyName*/ undefined, (<UnionType>type).types);
10554+
const widenedTypes = sameMap((<UnionType>type).types, t => t.flags & TypeFlags.Nullable ? t : getWidenedTypeWithContext(t, unionContext));
10555+
// Widening an empty object literal transitions from a highly restrictive type to
10556+
// a highly inclusive one. For that reason we perform subtype reduction here if the
10557+
// union includes empty object types (e.g. reducing {} | string to just {}).
10558+
return getUnionType(widenedTypes, some(widenedTypes, isEmptyObjectType));
1048410559
}
1048510560
if (isArrayType(type) || isTupleType(type)) {
1048610561
return createTypeReference((<TypeReference>type).target, sameMap((<TypeReference>type).typeArguments, getWidenedType));
@@ -10502,28 +10577,35 @@ namespace ts {
1050210577
*/
1050310578
function reportWideningErrorsInType(type: Type): boolean {
1050410579
let errorReported = false;
10505-
if (type.flags & TypeFlags.Union) {
10506-
for (const t of (<UnionType>type).types) {
10507-
if (reportWideningErrorsInType(t)) {
10580+
if (type.flags & TypeFlags.ContainsWideningType) {
10581+
if (type.flags & TypeFlags.Union) {
10582+
if (some((<UnionType>type).types, isEmptyObjectType)) {
1050810583
errorReported = true;
1050910584
}
10585+
else {
10586+
for (const t of (<UnionType>type).types) {
10587+
if (reportWideningErrorsInType(t)) {
10588+
errorReported = true;
10589+
}
10590+
}
10591+
}
1051010592
}
10511-
}
10512-
if (isArrayType(type) || isTupleType(type)) {
10513-
for (const t of (<TypeReference>type).typeArguments) {
10514-
if (reportWideningErrorsInType(t)) {
10515-
errorReported = true;
10593+
if (isArrayType(type) || isTupleType(type)) {
10594+
for (const t of (<TypeReference>type).typeArguments) {
10595+
if (reportWideningErrorsInType(t)) {
10596+
errorReported = true;
10597+
}
1051610598
}
1051710599
}
10518-
}
10519-
if (getObjectFlags(type) & ObjectFlags.ObjectLiteral) {
10520-
for (const p of getPropertiesOfObjectType(type)) {
10521-
const t = getTypeOfSymbol(p);
10522-
if (t.flags & TypeFlags.ContainsWideningType) {
10523-
if (!reportWideningErrorsInType(t)) {
10524-
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, symbolName(p), typeToString(getWidenedType(t)));
10600+
if (isObjectLiteralType(type)) {
10601+
for (const p of getPropertiesOfObjectType(type)) {
10602+
const t = getTypeOfSymbol(p);
10603+
if (t.flags & TypeFlags.ContainsWideningType) {
10604+
if (!reportWideningErrorsInType(t)) {
10605+
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, symbolName(p), typeToString(getWidenedType(t)));
10606+
}
10607+
errorReported = true;
1052510608
}
10526-
errorReported = true;
1052710609
}
1052810610
}
1052910611
}
@@ -11029,11 +11111,28 @@ namespace ts {
1102911111
return constraint && maybeTypeOfKind(constraint, TypeFlags.Primitive | TypeFlags.Index);
1103011112
}
1103111113

11114+
function isObjectLiteralType(type: Type) {
11115+
return !!(getObjectFlags(type) & ObjectFlags.ObjectLiteral);
11116+
}
11117+
11118+
function widenObjectLiteralCandidates(candidates: Type[]): Type[] {
11119+
if (candidates.length > 1) {
11120+
const objectLiterals = filter(candidates, isObjectLiteralType);
11121+
if (objectLiterals.length) {
11122+
const objectLiteralsType = getWidenedType(getUnionType(objectLiterals, /*subtypeReduction*/ true));
11123+
return concatenate(filter(candidates, t => !isObjectLiteralType(t)), [objectLiteralsType]);
11124+
}
11125+
}
11126+
return candidates;
11127+
}
11128+
1103211129
function getInferredType(context: InferenceContext, index: number): Type {
1103311130
const inference = context.inferences[index];
1103411131
let inferredType = inference.inferredType;
1103511132
if (!inferredType) {
1103611133
if (inference.candidates) {
11134+
// Extract all object literal types and replace them with a single widened and normalized type.
11135+
const candidates = widenObjectLiteralCandidates(inference.candidates);
1103711136
// We widen inferred literal types if
1103811137
// all inferences were made to top-level ocurrences of the type parameter, and
1103911138
// the type parameter has no constraint or its constraint includes no primitive or literal types, and
@@ -11042,7 +11141,7 @@ namespace ts {
1104211141
const widenLiteralTypes = inference.topLevel &&
1104311142
!hasPrimitiveConstraint(inference.typeParameter) &&
1104411143
(inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter));
11045-
const baseCandidates = widenLiteralTypes ? sameMap(inference.candidates, getWidenedLiteralType) :inference.candidates;
11144+
const baseCandidates = widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) : candidates;
1104611145
// If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if
1104711146
// union types were requested or if all inferences were made from the return type position, infer a
1104811147
// union type. Otherwise, infer a common supertype.

‎src/compiler/types.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3327,6 +3327,7 @@ namespace ts {
33273327
ObjectLiteral=1<<7,// Originates in an object literal
33283328
EvolvingArray=1<<8,// Evolving array type
33293329
ObjectLiteralPatternWithComputedProperties=1<<9,// Object literal pattern with computed properties
3330+
ContainsSpread=1<<10,// Object literal contains spread operation
33303331
ClassOrInterface=Class|Interface
33313332
}
33323333

@@ -3609,6 +3610,14 @@ namespace ts {
36093610
compareTypes:TypeComparer;// Type comparer function
36103611
}
36113612

3613+
/*@internal */
3614+
exportinterfaceWideningContext{
3615+
parent?:WideningContext;// Parent context
3616+
propertyName?:__String;// Name of property in parent
3617+
siblings?:Type[];// Types of siblings
3618+
resolvedPropertyNames?:__String[];// Property names occurring in sibling object literals
3619+
}
3620+
36123621
/*@internal */
36133622
exportconstenumSpecialPropertyAssignmentKind{
36143623
None,

‎tests/baselines/reference/api/tsserverlibrary.d.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2040,6 +2040,7 @@ declare namespace ts {
20402040
ObjectLiteral=128,
20412041
EvolvingArray=256,
20422042
ObjectLiteralPatternWithComputedProperties=512,
2043+
ContainsSpread=1024,
20432044
ClassOrInterface=3,
20442045
}
20452046
interfaceObjectTypeextendsType{

‎tests/baselines/reference/api/typescript.d.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2040,6 +2040,7 @@ declare namespace ts {
20402040
ObjectLiteral=128,
20412041
EvolvingArray=256,
20422042
ObjectLiteralPatternWithComputedProperties=512,
2043+
ContainsSpread=1024,
20432044
ClassOrInterface=3,
20442045
}
20452046
interfaceObjectTypeextendsType{

‎tests/baselines/reference/asyncFunctionDeclaration10_es2017.errors.txt‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ tests/cases/conformance/async/es2017/functionDeclarations/asyncFunctionDeclarati
44
tests/cases/conformance/async/es2017/functionDeclarations/asyncFunctionDeclaration10_es2017.ts(1,33): error TS2304: Cannot find name 'await'.
55
tests/cases/conformance/async/es2017/functionDeclarations/asyncFunctionDeclaration10_es2017.ts(1,38): error TS1005: ';' expected.
66
tests/cases/conformance/async/es2017/functionDeclarations/asyncFunctionDeclaration10_es2017.ts(1,39): error TS1128: Declaration or statement expected.
7+
tests/cases/conformance/async/es2017/functionDeclarations/asyncFunctionDeclaration10_es2017.ts(1,41): error TS2365: Operator '>' cannot be applied to types 'boolean' and '{}'.
78
tests/cases/conformance/async/es2017/functionDeclarations/asyncFunctionDeclaration10_es2017.ts(1,49): error TS2532: Object is possibly 'undefined'.
89
tests/cases/conformance/async/es2017/functionDeclarations/asyncFunctionDeclaration10_es2017.ts(1,53): error TS1109: Expression expected.
910

1011

11-
==== tests/cases/conformance/async/es2017/functionDeclarations/asyncFunctionDeclaration10_es2017.ts (8 errors) ====
12+
==== tests/cases/conformance/async/es2017/functionDeclarations/asyncFunctionDeclaration10_es2017.ts (9 errors) ====
1213
async function foo(a = await => await): Promise<void> {
1314
~~~~~~~~~
1415
!!! error TS2371: A parameter initializer is only allowed in a function or constructor implementation.
@@ -22,8 +23,11 @@ tests/cases/conformance/async/es2017/functionDeclarations/asyncFunctionDeclarati
2223
!!! error TS1005: ';' expected.
2324
~
2425
!!! error TS1128: Declaration or statement expected.
26+
~~~~~~~~~~~~~~~
2527
~~~~
2628
!!! error TS2532: Object is possibly 'undefined'.
2729
~
2830
!!! error TS1109: Expression expected.
29-
}
31+
}
32+
~
33+
!!! error TS2365: Operator '>' cannot be applied to types 'boolean' and '{}'.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp