Skip to content

Commit

Permalink
Added comments and additional circularity tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Jan 14, 2017
1 parent 25cb02e commit ca16ba8
Show file tree
Hide file tree
Showing 8 changed files with 614 additions and 184 deletions.
86 changes: 67 additions & 19 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4840,15 +4840,60 @@ namespace ts {
}
}

function getDefaultOfTypeParameter(typeParameter: TypeParameter): Type {
/**
* Gets the default type for a type parameter.
*
* If the type parameter is the result of an instantiation, this gets the instantiated
* default type of its target. If the type parameter has no default type, `undefined`
* is returned.
*
* This function *does not* perform a circularity check.
*/
function getDefaultFromTypeParameter(typeParameter: TypeParameter): Type | undefined {
if (!typeParameter.default) {
if (typeParameter.target) {
const targetDefault = getDefaultFromTypeParameter(typeParameter.target);
typeParameter.default = targetDefault ? instantiateType(targetDefault, typeParameter.mapper) : noConstraintOrDefaultType;
}
else {
const defaultDeclaration = typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameter(decl) && decl.default);
typeParameter.default = defaultDeclaration ? getTypeFromTypeNode(defaultDeclaration) : noConstraintOrDefaultType;
}
}
return typeParameter.default === noConstraintOrDefaultType ? undefined : typeParameter.default;
}

/**
* Gets the default type for a type parameter.
*
* If the type parameter is the result of an instantiation, this gets the instantiated
* default type of its target. If the type parameter has no default type, or if the default
* type circularly references the type parameter, `undefined` is returned.
*
* This function *does* perform a circularity check.
*/
function getDefaultOfTypeParameter(typeParameter: TypeParameter): Type | undefined {
return hasNonCircularDefault(typeParameter) ? getDefaultFromTypeParameter(typeParameter) : undefined;
}

function hasNonCircularDefault(type: TypeParameter) {
return getResolvedDefault(type) !== circularConstraintOrDefaultType;
/**
* Determines whether a type parameter has a non-circular default type.
*
* Note that this function also returns `true` if a type parameter *does not* have a
* default type.
*/
function hasNonCircularDefault(typeParameter: TypeParameter): boolean {
return getResolvedDefault(typeParameter) !== circularConstraintOrDefaultType;
}

function getResolvedDefault(typeParameter: TypeParameter) {
/**
* Resolves the default type of a type parameter.
*
* If the type parameter has no default, the `noConstraintOrDefaultType` singleton is
* returned. If the type parameter has a circular default, the
* `circularConstraintOrDefaultType` singleton is returned.
*/
function getResolvedDefault(typeParameter: TypeParameter): Type {
if (!typeParameter.resolvedDefault) {
if (!pushTypeResolution(typeParameter, TypeSystemPropertyName.ResolvedDefault)) {
return circularConstraintOrDefaultType;
Expand All @@ -4863,6 +4908,12 @@ namespace ts {
return typeParameter.resolvedDefault;
}

/**
* Recursively resolves the default type for a type.
*
* If the type is a union or intersection type and any of its constituents is a circular
* reference, the `circularConstraintOrDefaultType` singleton is returned.
*/
function getResolvedDefaultWorker(type: Type): Type {
if (type.flags & TypeFlags.TypeParameter) {
return getResolvedDefault(<TypeParameter>type);
Expand Down Expand Up @@ -5173,6 +5224,14 @@ namespace ts {
return minTypeArgumentCount;
}

/**
* Fill in default types for unsupplied type arguments. If `typeArguments` is undefined
* when a default type is supplied, a new array will be created and returned.
*
* @param typeArguments The supplied type arguments.
* @param typeParameters The requested type parameters.
* @param minTypeArgumentCount The minimum number of required type arguments.
*/
function fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number) {
const numTypeParameters = typeParameters ? typeParameters.length : 0;
if (numTypeParameters) {
Expand Down Expand Up @@ -5488,20 +5547,6 @@ namespace ts {
return typeParameter.constraint === noConstraintOrDefaultType ? undefined : typeParameter.constraint;
}

function getDefaultFromTypeParameter(typeParameter: TypeParameter): Type | undefined {
if (!typeParameter.default) {
if (typeParameter.target) {
const targetDefault = getDefaultFromTypeParameter(typeParameter.target);
typeParameter.default = targetDefault ? instantiateType(targetDefault, typeParameter.mapper) : noConstraintOrDefaultType;
}
else {
const defaultDeclaration = typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameter(decl) && decl.default);
typeParameter.default = defaultDeclaration ? getTypeFromTypeNode(defaultDeclaration) : noConstraintOrDefaultType;
}
}
return typeParameter.default === noConstraintOrDefaultType ? undefined : typeParameter.default;
}

function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol {
return getSymbolOfNode(getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter).parent);
}
Expand Down Expand Up @@ -13535,6 +13580,7 @@ namespace ts {
? createInferenceContext(originalCandidate, /*inferUnionTypes*/ false)
: undefined;

// fix each supplied type argument in the inference context
if (typeArguments) {
for (let i = 0; i < typeArguments.length; i++) {
inferenceContext.inferredTypes[i] = getTypeFromTypeNode(typeArguments[i]);
Expand All @@ -13545,8 +13591,10 @@ namespace ts {
while (true) {
candidate = originalCandidate;
if (candidate.typeParameters) {
typeArgumentsAreValid = typeArguments ? checkTypeArguments(candidate, typeArguments, inferenceContext.inferredTypes, /*reportErrors*/ false) : true;
// Check any supplied type arguments against the candidate.
typeArgumentsAreValid = !typeArguments || checkTypeArguments(candidate, typeArguments, inferenceContext.inferredTypes, /*reportErrors*/ false);
if (typeArgumentsAreValid) {
// Infer any unsupplied type arguments for the candidate.
inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext);
typeArgumentsAreValid = inferenceContext.failedTypeParameterIndex === undefined;
}
Expand Down
80 changes: 80 additions & 0 deletions tests/baselines/reference/genericDefaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,28 @@ const f03c06 = f03<number, number>();
const f03c07 = f03<number, number>(1);
const f03c08 = f03<number, number>(1, 2);

declare function f04<T, U = T | { a: number }>(a?: T, b?: U): [T, U];
const f04c00 = f04();
const f04c01 = f04(1);
const f04c02 = f04(1, 1);
const f04c03 = f04<number>();
const f04c04 = f04<number>(1);
const f04c05 = f04<number>(1, 2);
const f04c06 = f04<number, number>();
const f04c07 = f04<number, number>(1);
const f04c08 = f04<number, number>(1, 2);

declare function f05<T, U = T & { a: number }>(a?: T, b?: U): [T, U];
const f05c00 = f05();
const f05c01 = f05(1);
const f05c02 = f05(1, 1);
const f05c03 = f05<number>();
const f05c04 = f05<number>(1);
const f05c05 = f05<number>(1, 2);
const f05c06 = f05<number, number>();
const f05c07 = f05<number, number>(1);
const f05c08 = f05<number, number>(1, 2);

interface i00<T = number> { a: T; }
const i00c00 = (<i00>x).a;
const i00c01 = (<i00<number>>x).a;
Expand Down Expand Up @@ -120,6 +142,24 @@ var f03c05 = f03(1, 2);
var f03c06 = f03();
var f03c07 = f03(1);
var f03c08 = f03(1, 2);
var f04c00 = f04();
var f04c01 = f04(1);
var f04c02 = f04(1, 1);
var f04c03 = f04();
var f04c04 = f04(1);
var f04c05 = f04(1, 2);
var f04c06 = f04();
var f04c07 = f04(1);
var f04c08 = f04(1, 2);
var f05c00 = f05();
var f05c01 = f05(1);
var f05c02 = f05(1, 1);
var f05c03 = f05();
var f05c04 = f05(1);
var f05c05 = f05(1, 2);
var f05c06 = f05();
var f05c07 = f05(1);
var f05c08 = f05(1, 2);
var i00c00 = x.a;
var i00c01 = x.a;
var i01c00 = x.a;
Expand Down Expand Up @@ -187,6 +227,46 @@ declare const f03c05: [number, number];
declare const f03c06: [number, number];
declare const f03c07: [number, number];
declare const f03c08: [number, number];
declare function f04<T, U = T | {
a: number;
}>(a?: T, b?: U): [T, U];
declare const f04c00: [{}, {} | {
a: number;
}];
declare const f04c01: [number, number | {
a: number;
}];
declare const f04c02: [number, number];
declare const f04c03: [number, number | {
a: number;
}];
declare const f04c04: [number, number | {
a: number;
}];
declare const f04c05: [number, number];
declare const f04c06: [number, number];
declare const f04c07: [number, number];
declare const f04c08: [number, number];
declare function f05<T, U = T & {
a: number;
}>(a?: T, b?: U): [T, U];
declare const f05c00: [{}, {} & {
a: number;
}];
declare const f05c01: [number, number & {
a: number;
}];
declare const f05c02: [number, number];
declare const f05c03: [number, number & {
a: number;
}];
declare const f05c04: [number, number & {
a: number;
}];
declare const f05c05: [number, number];
declare const f05c06: [number, number];
declare const f05c07: [number, number];
declare const f05c08: [number, number];
interface i00<T = number> {
a: T;
}
Expand Down
Loading

0 comments on commit ca16ba8

Please sign in to comment.