Skip to content

Commit cacc300

Browse files
committed
Merge branch 'master' into implicitlyExcludeNodeModules
2 parents b07aa0d + 31230b9 commit cacc300

File tree

45 files changed

+1263
-84
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1263
-84
lines changed

src/compiler/checker.ts

Lines changed: 110 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5788,8 +5788,7 @@ namespace ts {
57885788
}
57895789

57905790
function isGenericMappedType(type: Type) {
5791-
return getObjectFlags(type) & ObjectFlags.Mapped &&
5792-
maybeTypeOfKind(getConstraintTypeFromMappedType(<MappedType>type), TypeFlags.TypeVariable | TypeFlags.Index);
5791+
return getObjectFlags(type) & ObjectFlags.Mapped && isGenericIndexType(getConstraintTypeFromMappedType(<MappedType>type));
57935792
}
57945793

57955794
function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
@@ -5901,6 +5900,10 @@ namespace ts {
59015900
}
59025901

59035902
function getConstraintOfIndexedAccess(type: IndexedAccessType) {
5903+
const transformed = getTransformedIndexedAccessType(type);
5904+
if (transformed) {
5905+
return transformed;
5906+
}
59045907
const baseObjectType = getBaseConstraintOfType(type.objectType);
59055908
const baseIndexType = getBaseConstraintOfType(type.indexType);
59065909
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
@@ -5972,11 +5975,18 @@ namespace ts {
59725975
return stringType;
59735976
}
59745977
if (t.flags & TypeFlags.IndexedAccess) {
5978+
const transformed = getTransformedIndexedAccessType(<IndexedAccessType>t);
5979+
if (transformed) {
5980+
return getBaseConstraint(transformed);
5981+
}
59755982
const baseObjectType = getBaseConstraint((<IndexedAccessType>t).objectType);
59765983
const baseIndexType = getBaseConstraint((<IndexedAccessType>t).indexType);
59775984
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined;
59785985
return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined;
59795986
}
5987+
if (isGenericMappedType(t)) {
5988+
return emptyObjectType;
5989+
}
59805990
return t;
59815991
}
59825992
}
@@ -7604,26 +7614,73 @@ namespace ts {
76047614
return instantiateType(getTemplateTypeFromMappedType(type), templateMapper);
76057615
}
76067616

7607-
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
7608-
// If the index type is generic, if the object type is generic and doesn't originate in an expression,
7609-
// or if the object type is a mapped type with a generic constraint, we are performing a higher-order
7610-
// index access where we cannot meaningfully access the properties of the object type. Note that for a
7611-
// generic T and a non-generic K, we eagerly resolve T[K] if it originates in an expression. This is to
7612-
// preserve backwards compatibility. For example, an element access 'this["foo"]' has always been resolved
7613-
// eagerly using the constraint type of 'this' at the given location.
7614-
if (maybeTypeOfKind(indexType, TypeFlags.TypeVariable | TypeFlags.Index) ||
7615-
maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) ||
7616-
isGenericMappedType(objectType)) {
7617+
function isGenericObjectType(type: Type): boolean {
7618+
return type.flags & TypeFlags.TypeVariable ? true :
7619+
getObjectFlags(type) & ObjectFlags.Mapped ? isGenericIndexType(getConstraintTypeFromMappedType(<MappedType>type)) :
7620+
type.flags & TypeFlags.UnionOrIntersection ? forEach((<UnionOrIntersectionType>type).types, isGenericObjectType) :
7621+
false;
7622+
}
7623+
7624+
function isGenericIndexType(type: Type): boolean {
7625+
return type.flags & (TypeFlags.TypeVariable | TypeFlags.Index) ? true :
7626+
type.flags & TypeFlags.UnionOrIntersection ? forEach((<UnionOrIntersectionType>type).types, isGenericIndexType) :
7627+
false;
7628+
}
7629+
7630+
// Return true if the given type is a non-generic object type with a string index signature and no
7631+
// other members.
7632+
function isStringIndexOnlyType(type: Type) {
7633+
if (type.flags & TypeFlags.Object && !isGenericMappedType(type)) {
7634+
const t = resolveStructuredTypeMembers(<ObjectType>type);
7635+
return t.properties.length === 0 &&
7636+
t.callSignatures.length === 0 && t.constructSignatures.length === 0 &&
7637+
t.stringIndexInfo && !t.numberIndexInfo;
7638+
}
7639+
return false;
7640+
}
7641+
7642+
// Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
7643+
// more object types with only a string index signature, e.g. '(U & V & { [x: string]: D })[K]', return a
7644+
// transformed type of the form '(U & V)[K] | D'. This allows us to properly reason about higher order indexed
7645+
// access types with default property values as expressed by D.
7646+
function getTransformedIndexedAccessType(type: IndexedAccessType): Type {
7647+
const objectType = type.objectType;
7648+
if (objectType.flags & TypeFlags.Intersection && isGenericObjectType(objectType) && some((<IntersectionType>objectType).types, isStringIndexOnlyType)) {
7649+
const regularTypes: Type[] = [];
7650+
const stringIndexTypes: Type[] = [];
7651+
for (const t of (<IntersectionType>objectType).types) {
7652+
if (isStringIndexOnlyType(t)) {
7653+
stringIndexTypes.push(getIndexTypeOfType(t, IndexKind.String));
7654+
}
7655+
else {
7656+
regularTypes.push(t);
7657+
}
7658+
}
7659+
return getUnionType([
7660+
getIndexedAccessType(getIntersectionType(regularTypes), type.indexType),
7661+
getIntersectionType(stringIndexTypes)
7662+
]);
7663+
}
7664+
return undefined;
7665+
}
7666+
7667+
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode): Type {
7668+
// If the object type is a mapped type { [P in K]: E }, where K is generic, we instantiate E using a mapper
7669+
// that substitutes the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we
7670+
// construct the type Box<T[X]>.
7671+
if (isGenericMappedType(objectType)) {
7672+
return getIndexedAccessForMappedType(<MappedType>objectType, indexType, accessNode);
7673+
}
7674+
// Otherwise, if the index type is generic, or if the object type is generic and doesn't originate in an
7675+
// expression, we are performing a higher-order index access where we cannot meaningfully access the properties
7676+
// of the object type. Note that for a generic T and a non-generic K, we eagerly resolve T[K] if it originates
7677+
// in an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]'
7678+
// has always been resolved eagerly using the constraint type of 'this' at the given location.
7679+
if (isGenericIndexType(indexType) || !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) && isGenericObjectType(objectType)) {
76177680
if (objectType.flags & TypeFlags.Any) {
76187681
return objectType;
76197682
}
7620-
// If the object type is a mapped type { [P in K]: E }, we instantiate E using a mapper that substitutes
7621-
// the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we construct the
7622-
// type Box<T[X]>.
7623-
if (isGenericMappedType(objectType)) {
7624-
return getIndexedAccessForMappedType(<MappedType>objectType, indexType, accessNode);
7625-
}
7626-
// Otherwise we defer the operation by creating an indexed access type.
7683+
// Defer the operation by creating an indexed access type.
76277684
const id = objectType.id + "," + indexType.id;
76287685
let type = indexedAccessTypes.get(id);
76297686
if (!type) {
@@ -8017,7 +8074,7 @@ namespace ts {
80178074

80188075
function cloneTypeMapper(mapper: TypeMapper): TypeMapper {
80198076
return mapper && isInferenceContext(mapper) ?
8020-
createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.inferences) :
8077+
createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.compareTypes, mapper.inferences) :
80218078
mapper;
80228079
}
80238080

@@ -8458,7 +8515,7 @@ namespace ts {
84588515
ignoreReturnTypes: boolean,
84598516
reportErrors: boolean,
84608517
errorReporter: ErrorReporter,
8461-
compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary {
8518+
compareTypes: TypeComparer): Ternary {
84628519
// TODO (drosen): De-duplicate code between related functions.
84638520
if (source === target) {
84648521
return Ternary.True;
@@ -8468,7 +8525,7 @@ namespace ts {
84688525
}
84698526

84708527
if (source.typeParameters) {
8471-
source = instantiateSignatureInContextOf(source, target);
8528+
source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes);
84728529
}
84738530

84748531
let result = Ternary.True;
@@ -9614,6 +9671,11 @@ namespace ts {
96149671
if (sourceInfo) {
96159672
return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors);
96169673
}
9674+
if (isGenericMappedType(source)) {
9675+
// A generic mapped type { [P in K]: T } is related to an index signature { [x: string]: U }
9676+
// if T is related to U.
9677+
return kind === IndexKind.String && isRelatedTo(getTemplateTypeFromMappedType(<MappedType>source), targetInfo.type, reportErrors);
9678+
}
96179679
if (isObjectLiteralType(source)) {
96189680
let related = Ternary.True;
96199681
if (kind === IndexKind.String) {
@@ -10216,13 +10278,14 @@ namespace ts {
1021610278
}
1021710279
}
1021810280

10219-
function createInferenceContext(signature: Signature, flags: InferenceFlags, baseInferences?: InferenceInfo[]): InferenceContext {
10281+
function createInferenceContext(signature: Signature, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext {
1022010282
const inferences = baseInferences ? map(baseInferences, cloneInferenceInfo) : map(signature.typeParameters, createInferenceInfo);
1022110283
const context = mapper as InferenceContext;
1022210284
context.mappedTypes = signature.typeParameters;
1022310285
context.signature = signature;
1022410286
context.inferences = inferences;
1022510287
context.flags = flags;
10288+
context.compareTypes = compareTypes || compareTypesAssignable;
1022610289
return context;
1022710290

1022810291
function mapper(t: Type): Type {
@@ -10325,6 +10388,19 @@ namespace ts {
1032510388
}
1032610389
}
1032710390

10391+
function isPossiblyAssignableTo(source: Type, target: Type) {
10392+
const properties = getPropertiesOfObjectType(target);
10393+
for (const targetProp of properties) {
10394+
if (!(targetProp.flags & (SymbolFlags.Optional | SymbolFlags.Prototype))) {
10395+
const sourceProp = getPropertyOfObjectType(source, targetProp.escapedName);
10396+
if (!sourceProp) {
10397+
return false;
10398+
}
10399+
}
10400+
}
10401+
return true;
10402+
}
10403+
1032810404
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) {
1032910405
let symbolStack: Symbol[];
1033010406
let visited: Map<boolean>;
@@ -10518,10 +10594,14 @@ namespace ts {
1051810594
return;
1051910595
}
1052010596
}
10521-
inferFromProperties(source, target);
10522-
inferFromSignatures(source, target, SignatureKind.Call);
10523-
inferFromSignatures(source, target, SignatureKind.Construct);
10524-
inferFromIndexTypes(source, target);
10597+
// Infer from the members of source and target only if the two types are possibly related. We check
10598+
// in both directions because we may be inferring for a co-variant or a contra-variant position.
10599+
if (isPossiblyAssignableTo(source, target) || isPossiblyAssignableTo(target, source)) {
10600+
inferFromProperties(source, target);
10601+
inferFromSignatures(source, target, SignatureKind.Call);
10602+
inferFromSignatures(source, target, SignatureKind.Construct);
10603+
inferFromIndexTypes(source, target);
10604+
}
1052510605
}
1052610606

1052710607
function inferFromProperties(source: Type, target: Type) {
@@ -10653,7 +10733,7 @@ namespace ts {
1065310733
const constraint = getConstraintOfTypeParameter(context.signature.typeParameters[index]);
1065410734
if (constraint) {
1065510735
const instantiatedConstraint = instantiateType(constraint, context);
10656-
if (!isTypeAssignableTo(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
10736+
if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
1065710737
inference.inferredType = inferredType = instantiatedConstraint;
1065810738
}
1065910739
}
@@ -15054,8 +15134,8 @@ namespace ts {
1505415134
}
1505515135

1505615136
// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
15057-
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper): Signature {
15058-
const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes);
15137+
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper, compareTypes?: TypeComparer): Signature {
15138+
const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes, compareTypes);
1505915139
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
1506015140
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
1506115141
inferTypes(context.inferences, instantiateType(source, contextualMapper || identityMapper), target);

src/compiler/core.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,6 @@ namespace ts {
1111

1212
/* @internal */
1313
namespace ts {
14-
/**
15-
* Ternary values are defined such that
16-
* x & y is False if either x or y is False.
17-
* x & y is Maybe if either x or y is Maybe, but neither x or y is False.
18-
* x & y is True if both x and y are True.
19-
* x | y is False if both x and y are False.
20-
* x | y is Maybe if either x or y is Maybe, but neither x or y is True.
21-
* x | y is True if either x or y is True.
22-
*/
23-
export const enum Ternary {
24-
False = 0,
25-
Maybe = 1,
26-
True = -1
27-
}
2814

2915
// More efficient to create a collator once and use its `compare` than to call `a.localeCompare(b)` many times.
3016
export const collator: { compare(a: string, b: string): number } = typeof Intl === "object" && typeof Intl.Collator === "function" ? new Intl.Collator(/*locales*/ undefined, { usage: "sort", sensitivity: "accent" }) : undefined;

src/compiler/emitter.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,7 +1346,9 @@ namespace ts {
13461346

13471347
emitExpression(node.left);
13481348
increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined);
1349+
emitLeadingCommentsOfPosition(node.operatorToken.pos);
13491350
writeTokenNode(node.operatorToken);
1351+
emitTrailingCommentsOfPosition(node.operatorToken.end);
13501352
increaseIndentIf(indentAfterOperator, " ");
13511353
emitExpression(node.right);
13521354
decreaseIndentIf(indentBeforeOperator, indentAfterOperator);

src/compiler/parser.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2061,7 +2061,7 @@ namespace ts {
20612061
return <TemplateMiddle | TemplateTail>fragment;
20622062
}
20632063

2064-
function parseLiteralLikeNode(kind: SyntaxKind): LiteralLikeNode {
2064+
function parseLiteralLikeNode(kind: SyntaxKind): LiteralExpression | LiteralLikeNode {
20652065
const node = <LiteralExpression>createNode(kind);
20662066
const text = scanner.getTokenValue();
20672067
node.text = text;
@@ -2611,11 +2611,31 @@ namespace ts {
26112611
return token() === SyntaxKind.DotToken ? undefined : node;
26122612
}
26132613

2614-
function parseLiteralTypeNode(): LiteralTypeNode {
2615-
const node = <LiteralTypeNode>createNode(SyntaxKind.LiteralType);
2616-
node.literal = parseSimpleUnaryExpression();
2617-
finishNode(node);
2618-
return node;
2614+
function parseLiteralTypeNode(negative?: boolean): LiteralTypeNode {
2615+
const node = createNode(SyntaxKind.LiteralType) as LiteralTypeNode;
2616+
let unaryMinusExpression: PrefixUnaryExpression;
2617+
if (negative) {
2618+
unaryMinusExpression = createNode(SyntaxKind.PrefixUnaryExpression) as PrefixUnaryExpression;
2619+
unaryMinusExpression.operator = SyntaxKind.MinusToken;
2620+
nextToken();
2621+
}
2622+
let expression: UnaryExpression;
2623+
switch (token()) {
2624+
case SyntaxKind.StringLiteral:
2625+
case SyntaxKind.NumericLiteral:
2626+
expression = parseLiteralLikeNode(token()) as LiteralExpression;
2627+
break;
2628+
case SyntaxKind.TrueKeyword:
2629+
case SyntaxKind.FalseKeyword:
2630+
expression = parseTokenNode();
2631+
}
2632+
if (negative) {
2633+
unaryMinusExpression.operand = expression;
2634+
finishNode(unaryMinusExpression);
2635+
expression = unaryMinusExpression;
2636+
}
2637+
node.literal = expression;
2638+
return finishNode(node);
26192639
}
26202640

26212641
function nextTokenIsNumericLiteral() {
@@ -2650,7 +2670,7 @@ namespace ts {
26502670
case SyntaxKind.FalseKeyword:
26512671
return parseLiteralTypeNode();
26522672
case SyntaxKind.MinusToken:
2653-
return lookAhead(nextTokenIsNumericLiteral) ? parseLiteralTypeNode() : parseTypeReference();
2673+
return lookAhead(nextTokenIsNumericLiteral) ? parseLiteralTypeNode(/*negative*/ true) : parseTypeReference();
26542674
case SyntaxKind.VoidKeyword:
26552675
case SyntaxKind.NullKeyword:
26562676
return parseTokenNode<TypeNode>();

0 commit comments

Comments
 (0)