Skip to content

Adds support for type parameter defaults #13487

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Feb 15, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3d3dae0
Adds support for type parameter defaults
rbuckton Jan 14, 2017
442f540
Updated Promise and PromiseLike to use defaults
rbuckton Jan 14, 2017
25cb02e
Fix circularity check, simplify default type mapper
rbuckton Jan 14, 2017
ca16ba8
Added comments and additional circularity tests
rbuckton Jan 14, 2017
0b44a2c
Flexible declaration merging
rbuckton Jan 19, 2017
0500065
Avoid inference for fully-supplied type arguments
rbuckton Jan 20, 2017
5ff0f81
Diagnostic message punctuation
rbuckton Jan 20, 2017
a2be5e2
Report error using type parameter from merged declaration
rbuckton Jan 21, 2017
fd228a9
Remove partial inference
rbuckton Jan 21, 2017
6b2c8cb
Defaults for type aliases
rbuckton Jan 21, 2017
76ba6a7
Merge branch 'master' into genericDefaults
rbuckton Jan 21, 2017
15232fe
Remove circular default check
rbuckton Jan 24, 2017
f5f1c7e
Merge branch 'genericDefaults' of https://github.com/Microsoft/TypeSc…
rbuckton Jan 24, 2017
febde3f
Revert noConstraintType name change
rbuckton Jan 24, 2017
b58ef9e
Merge branch 'master' into genericDefaults
rbuckton Jan 30, 2017
7616e37
Use length() throught checker
rbuckton Jan 30, 2017
e001258
Move non-local type parameter check to resolveName
rbuckton Jan 30, 2017
9ba2a6b
Skip type parameters.
rbuckton Feb 1, 2017
6091050
Remove pre-computation of minTypeArgumentCount
rbuckton Feb 3, 2017
6ffcbf5
Merge branch 'master' into genericDefaults
rbuckton Feb 3, 2017
5bb2fe0
Simplify checkTypeParameterListsIdentical
rbuckton Feb 4, 2017
96181c0
Shortcut for class/namespace merge
rbuckton Feb 4, 2017
23216f9
Merge branch 'master' into genericDefaults
rbuckton Feb 15, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Added comments and additional circularity tests
  • Loading branch information
rbuckton committed Jan 14, 2017
commit ca16ba8fe7b2f2bd482a2141d530c3f6cbd114b4
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a little confusing - so if there were any circular types in a union, you return undefined. But you disregard all circularities in an intersection type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reused this from line 4827, above. It does seem like it should also be performing a comparison against types.length as well, however I am unsure as to whether there was a specific reason for 4827 to ignore circularity in an intersection type.

Copy link
Contributor Author

@rbuckton rbuckton Jan 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this more consistent in 25cb02e and I've added some additional circularity tests in ca16ba8.

}

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