Skip to content

Infer instance type arguments as constraint #49863

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

Closed
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
35 changes: 29 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1448,6 +1448,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
var noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny");
var noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis");
var useUnknownInCatchVariables = getStrictOptionValue(compilerOptions, "useUnknownInCatchVariables");
var strictInstanceOfTypeParameters = getStrictOptionValue(compilerOptions, "strictInstanceOfTypeParameters");
Copy link
Member

Choose a reason for hiding this comment

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

Let's just make this true for iterating on this PR.

Suggested change
var strictInstanceOfTypeParameters = getStrictOptionValue(compilerOptions, "strictInstanceOfTypeParameters");
var strictInstanceOfTypeParameters = true;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Applied this change manually with a comment. For some reason the UI wouldn't let me commit your suggestion 🤷

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 can also update the baselines to match this change temporarily if that's blocking the test suite in some way.

Copy link
Member

Choose a reason for hiding this comment

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

If we're still only checking the extended test suite (top100, user tests, rwc, DT, perf, etc), there's no need to update the baselines; they're all separate test suites which operate independently.

var keyofStringsOnly = !!compilerOptions.keyofStringsOnly;
var freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : ObjectFlags.FreshLiteral;
var exactOptionalPropertyTypes = compilerOptions.exactOptionalPropertyTypes;
Expand Down Expand Up @@ -10136,13 +10137,35 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
})!.parent;
}

function getInstanceTypeOfClassSymbol(classSymbol: Symbol): Type {
const classType = getDeclaredTypeOfSymbol(classSymbol) as GenericType;
const objectFlags = getObjectFlags(classType);
if (!(objectFlags & ObjectFlags.ClassOrInterface) || !classType.typeParameters) {
return classType;
}
const variances = getVariances(classType);
const typeArguments = map(classType.typeParameters, (typeParameter, i) => {
if (!strictInstanceOfTypeParameters) {
return anyType;
}
const variance = variances[i];
switch (variance & VarianceFlags.VarianceMask) {
case VarianceFlags.Contravariant:
case VarianceFlags.Bivariant:
return neverType;
}
return getBaseConstraintOfType(typeParameter) || unknownType;
});
return createTypeReference(classType, typeArguments);
}

function getTypeOfPrototypeProperty(prototype: Symbol): Type {
// TypeScript 1.0 spec (April 2014): 8.4
// Every class automatically contains a static property member named 'prototype',
// the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter.
// the type of which is an instantiation of the class type.
// Type parameters on this class are instantiated with a type based on their constraint and variance.
// It is an error to explicitly declare a static property member with the name 'prototype'.
const classType = getDeclaredTypeOfSymbol(getParentOfSymbol(prototype)!) as InterfaceType;
return classType.typeParameters ? createTypeReference(classType as GenericType, map(classType.typeParameters, _ => anyType)) : classType;
const classSymbol = getParentOfSymbol(prototype)!;
return getInstanceTypeOfClassSymbol(classSymbol);
}

// Return the type of the given property in the given type, or undefined if no such property exists
Expand Down Expand Up @@ -26951,10 +26974,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (symbol === undefined) {
return type;
}
const classSymbol = symbol.parent!;
const classSymbol = getParentOfSymbol(symbol)!;
const targetType = hasStaticModifier(Debug.checkDefined(symbol.valueDeclaration, "should always have a declaration"))
? getTypeOfSymbol(classSymbol) as InterfaceType
: getDeclaredTypeOfSymbol(classSymbol);
: getInstanceTypeOfClassSymbol(classSymbol);
return getNarrowedType(type, targetType, assumeTrue, /*checkDerived*/ true);
}

Expand Down
10 changes: 10 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,16 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
description: Diagnostics.Default_catch_clause_variables_as_unknown_instead_of_any,
defaultValueDescription: false,
},
{
name: "strictInstanceOfTypeParameters",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsBuildInfo: true,
strictFlag: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Default_type_arguments_to_parameter_constraint_or_unknown_instead_of_any_for_instance_checks,
defaultValueDescription: false,
},
{
name: "alwaysStrict",
type: "boolean",
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -6071,7 +6071,10 @@
"category": "Message",
"code": 6804
},

"Default type arguments to parameter constraint or 'unknown' instead of 'any' for instance checks.": {
"category": "Message",
"code": 6805
},
"one of:": {
"category": "Message",
"code": 6900
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7108,6 +7108,7 @@ export interface CompilerOptions {
strictBindCallApply?: boolean; // Always combine with strict property
strictNullChecks?: boolean; // Always combine with strict property
strictPropertyInitialization?: boolean; // Always combine with strict property
strictInstanceOfTypeParameters?: boolean;
stripInternal?: boolean;
suppressExcessPropertyErrors?: boolean;
suppressImplicitAnyIndexErrors?: boolean;
Expand Down
1 change: 1 addition & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8490,6 +8490,7 @@ export type StrictOptionName =
| "strictPropertyInitialization"
| "alwaysStrict"
| "useUnknownInCatchVariables"
| "strictInstanceOfTypeParameters"
;

/** @internal */
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/accessorsOverrideProperty9.types
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
}

return MixedClass;
>MixedClass : ((abstract new (...args: any[]) => MixedClass) & { prototype: ApiItemContainerMixin<any>.MixedClass; }) & TBaseClass
>MixedClass : ((abstract new (...args: any[]) => MixedClass) & { prototype: ApiItemContainerMixin<IApiItemConstructor>.MixedClass; }) & TBaseClass
}

// Subclass inheriting from mixin
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7135,6 +7135,7 @@ declare namespace ts {
strictBindCallApply?: boolean;
strictNullChecks?: boolean;
strictPropertyInitialization?: boolean;
strictInstanceOfTypeParameters?: boolean;
stripInternal?: boolean;
suppressExcessPropertyErrors?: boolean;
suppressImplicitAnyIndexErrors?: boolean;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3160,6 +3160,7 @@ declare namespace ts {
strictBindCallApply?: boolean;
strictNullChecks?: boolean;
strictPropertyInitialization?: boolean;
strictInstanceOfTypeParameters?: boolean;
stripInternal?: boolean;
suppressExcessPropertyErrors?: boolean;
suppressImplicitAnyIndexErrors?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "strictInstanceOfTypeParameters": true, /* Default type arguments to parameter constraint or 'unknown' instead of 'any' for instance checks. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "strictInstanceOfTypeParameters": true, /* Default type arguments to parameter constraint or 'unknown' instead of 'any' for instance checks. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "strictInstanceOfTypeParameters": true, /* Default type arguments to parameter constraint or 'unknown' instead of 'any' for instance checks. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "strictInstanceOfTypeParameters": true, /* Default type arguments to parameter constraint or 'unknown' instead of 'any' for instance checks. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "strictInstanceOfTypeParameters": true, /* Default type arguments to parameter constraint or 'unknown' instead of 'any' for instance checks. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
"noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "strictInstanceOfTypeParameters": true, /* Default type arguments to parameter constraint or 'unknown' instead of 'any' for instance checks. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "strictInstanceOfTypeParameters": true, /* Default type arguments to parameter constraint or 'unknown' instead of 'any' for instance checks. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "strictInstanceOfTypeParameters": true, /* Default type arguments to parameter constraint or 'unknown' instead of 'any' for instance checks. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "strictInstanceOfTypeParameters": true, /* Default type arguments to parameter constraint or 'unknown' instead of 'any' for instance checks. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "strictInstanceOfTypeParameters": true, /* Default type arguments to parameter constraint or 'unknown' instead of 'any' for instance checks. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "strictInstanceOfTypeParameters": true, /* Default type arguments to parameter constraint or 'unknown' instead of 'any' for instance checks. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"strictInstanceOfTypeParameters": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"strictInstanceOfTypeParameters": true
}
}
16 changes: 8 additions & 8 deletions tests/baselines/reference/expressionWithJSDocTypeArguments.types
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,23 @@ type TComeOnFoo = typeof foo<?string?>;
>foo : <T>(x: T) => T

const WhatBar = Bar<?>;
>WhatBar : { new (x: any): Bar<any>; prototype: Bar<any>; }
>Bar<?> : { new (x: any): Bar<any>; prototype: Bar<any>; }
>WhatBar : { new (x: any): Bar<any>; prototype: Bar<unknown>; }
>Bar<?> : { new (x: any): Bar<any>; prototype: Bar<unknown>; }
>Bar : typeof Bar

const HuhBar = Bar<string?>;
>HuhBar : { new (x: string | null): Bar<string | null>; prototype: Bar<any>; }
>Bar<string?> : { new (x: string | null): Bar<string | null>; prototype: Bar<any>; }
>HuhBar : { new (x: string | null): Bar<string | null>; prototype: Bar<unknown>; }
>Bar<string?> : { new (x: string | null): Bar<string | null>; prototype: Bar<unknown>; }
>Bar : typeof Bar

const NopeBar = Bar<?string>;
>NopeBar : { new (x: string | null): Bar<string | null>; prototype: Bar<any>; }
>Bar<?string> : { new (x: string | null): Bar<string | null>; prototype: Bar<any>; }
>NopeBar : { new (x: string | null): Bar<string | null>; prototype: Bar<unknown>; }
>Bar<?string> : { new (x: string | null): Bar<string | null>; prototype: Bar<unknown>; }
>Bar : typeof Bar

const ComeOnBar = Bar<?string?>;
>ComeOnBar : { new (x: string | null): Bar<string | null>; prototype: Bar<any>; }
>Bar<?string?> : { new (x: string | null): Bar<string | null>; prototype: Bar<any>; }
>ComeOnBar : { new (x: string | null): Bar<string | null>; prototype: Bar<unknown>; }
>Bar<?string?> : { new (x: string | null): Bar<string | null>; prototype: Bar<unknown>; }
>Bar : typeof Bar

type TWhatBar = typeof Bar<?>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpr
}

function f3() {
let c1 = C<string>; // { new (x: string): C<string>; f<U>(x: U): T[]; prototype: C<any>; }
let c1 = C<string>; // { new (x: string): C<string>; f<U>(x: U): T[]; prototype: C<unknown>; }
let f1 = C.f<string>; // (x: string) => string[]
}

Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/instantiationExpressions.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ declare class C<T> {
}

function f3() {
let c1 = C<string>; // { new (x: string): C<string>; f<U>(x: U): T[]; prototype: C<any>; }
let c1 = C<string>; // { new (x: string): C<string>; f<U>(x: U): T[]; prototype: C<unknown>; }
let f1 = C.f<string>; // (x: string) => string[]
}

Expand Down Expand Up @@ -187,7 +187,7 @@ function f2() {
var A2 = (Array); // Error
}
function f3() {
var c1 = (C); // { new (x: string): C<string>; f<U>(x: U): T[]; prototype: C<any>; }
var c1 = (C); // { new (x: string): C<string>; f<U>(x: U): T[]; prototype: C<unknown>; }
var f1 = (C.f); // (x: string) => string[]
}
function f10(f) {
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/instantiationExpressions.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ declare class C<T> {
function f3() {
>f3 : Symbol(f3, Decl(instantiationExpressions.ts, 29, 1))

let c1 = C<string>; // { new (x: string): C<string>; f<U>(x: U): T[]; prototype: C<any>; }
let c1 = C<string>; // { new (x: string): C<string>; f<U>(x: U): T[]; prototype: C<unknown>; }
>c1 : Symbol(c1, Decl(instantiationExpressions.ts, 32, 7))
>C : Symbol(C, Decl(instantiationExpressions.ts, 24, 40))

Expand Down
Loading