Skip to content

Commit

Permalink
Improve contextually typed parameters with initializers (microsoft#56506
Browse files Browse the repository at this point in the history
)
  • Loading branch information
Andarist authored Dec 5, 2023
1 parent 6c0687e commit 993ffd7
Show file tree
Hide file tree
Showing 15 changed files with 538 additions and 199 deletions.
13 changes: 10 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36221,9 +36221,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const len = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0);
for (let i = 0; i < len; i++) {
const parameter = signature.parameters[i];
if (!getEffectiveTypeAnnotationNode(parameter.valueDeclaration as ParameterDeclaration)) {
const contextualParameterType = tryGetTypeAtPosition(context, i);
assignParameterType(parameter, contextualParameterType);
const declaration = parameter.valueDeclaration as ParameterDeclaration;
if (!getEffectiveTypeAnnotationNode(declaration)) {
let type = tryGetTypeAtPosition(context, i);
if (type && declaration.initializer) {
let initializerType = checkDeclarationInitializer(declaration, CheckMode.Normal);
if (!isTypeAssignableTo(initializerType, type) && isTypeAssignableTo(type, initializerType = widenTypeInferredFromInitializer(declaration, initializerType))) {
type = initializerType;
}
}
assignParameterType(parameter, type);
}
}
if (signatureHasRestParameter(signature)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
contextuallyTypedParametersWithInitializers.ts(24,24): error TS7006: Parameter 'x' implicitly has an 'any' type.
contextuallyTypedParametersWithInitializers.ts(40,5): error TS7006: Parameter 'x' implicitly has an 'any' type.
contextuallyTypedParametersWithInitializers1.ts(24,24): error TS7006: Parameter 'x' implicitly has an 'any' type.
contextuallyTypedParametersWithInitializers1.ts(40,5): error TS7006: Parameter 'x' implicitly has an 'any' type.


==== contextuallyTypedParametersWithInitializers.ts (2 errors) ====
==== contextuallyTypedParametersWithInitializers1.ts (2 errors) ====
declare function id1<T>(input: T): T;
declare function id2<T extends (x: any) => any>(input: T): T;
declare function id3<T extends (x: { foo: any }) => any>(input: T): T;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//// [tests/cases/compiler/contextuallyTypedParametersWithInitializers.ts] ////
//// [tests/cases/compiler/contextuallyTypedParametersWithInitializers1.ts] ////

//// [contextuallyTypedParametersWithInitializers.ts]
//// [contextuallyTypedParametersWithInitializers1.ts]
declare function id1<T>(input: T): T;
declare function id2<T extends (x: any) => any>(input: T): T;
declare function id3<T extends (x: { foo: any }) => any>(input: T): T;
Expand Down Expand Up @@ -86,7 +86,7 @@ const fz1 = (debug = true) => false;
const fz2: Function = (debug = true) => false;


//// [contextuallyTypedParametersWithInitializers.js]
//// [contextuallyTypedParametersWithInitializers1.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeSomething = void 0;
Expand Down Expand Up @@ -234,5 +234,5 @@ var fz2 = function (debug) {
};


//// [contextuallyTypedParametersWithInitializers.d.ts]
//// [contextuallyTypedParametersWithInitializers1.d.ts]
export declare function executeSomething(): Promise<string>;

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//// [tests/cases/compiler/contextuallyTypedParametersWithInitializers.ts] ////
//// [tests/cases/compiler/contextuallyTypedParametersWithInitializers1.ts] ////

=== contextuallyTypedParametersWithInitializers.ts ===
=== contextuallyTypedParametersWithInitializers1.ts ===
declare function id1<T>(input: T): T;
>id1 : <T>(input: T) => T
>input : T
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//// [tests/cases/compiler/contextuallyTypedParametersWithInitializers2.ts] ////

=== contextuallyTypedParametersWithInitializers2.ts ===
declare function test1<
>test1 : Symbol(test1, Decl(contextuallyTypedParametersWithInitializers2.ts, 0, 0))

TContext,
>TContext : Symbol(TContext, Decl(contextuallyTypedParametersWithInitializers2.ts, 0, 23))

TMethods extends Record<string, (ctx: TContext, ...args: never[]) => unknown>,
>TMethods : Symbol(TMethods, Decl(contextuallyTypedParametersWithInitializers2.ts, 1, 11))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>ctx : Symbol(ctx, Decl(contextuallyTypedParametersWithInitializers2.ts, 2, 35))
>TContext : Symbol(TContext, Decl(contextuallyTypedParametersWithInitializers2.ts, 0, 23))
>args : Symbol(args, Decl(contextuallyTypedParametersWithInitializers2.ts, 2, 49))

>(context: TContext, methods: TMethods): void;
>context : Symbol(context, Decl(contextuallyTypedParametersWithInitializers2.ts, 3, 2))
>TContext : Symbol(TContext, Decl(contextuallyTypedParametersWithInitializers2.ts, 0, 23))
>methods : Symbol(methods, Decl(contextuallyTypedParametersWithInitializers2.ts, 3, 20))
>TMethods : Symbol(TMethods, Decl(contextuallyTypedParametersWithInitializers2.ts, 1, 11))

test1(
>test1 : Symbol(test1, Decl(contextuallyTypedParametersWithInitializers2.ts, 0, 0))
{
count: 0,
>count : Symbol(count, Decl(contextuallyTypedParametersWithInitializers2.ts, 6, 3))

},
{
checkLimit: (ctx, max = 500) => {},
>checkLimit : Symbol(checkLimit, Decl(contextuallyTypedParametersWithInitializers2.ts, 9, 3))
>ctx : Symbol(ctx, Decl(contextuallyTypedParametersWithInitializers2.ts, 10, 17))
>max : Symbol(max, Decl(contextuallyTypedParametersWithInitializers2.ts, 10, 21))

hasAccess: (ctx, user: { name: string }) => {},
>hasAccess : Symbol(hasAccess, Decl(contextuallyTypedParametersWithInitializers2.ts, 10, 39))
>ctx : Symbol(ctx, Decl(contextuallyTypedParametersWithInitializers2.ts, 11, 16))
>user : Symbol(user, Decl(contextuallyTypedParametersWithInitializers2.ts, 11, 20))
>name : Symbol(name, Decl(contextuallyTypedParametersWithInitializers2.ts, 11, 28))

},
);

declare const num: number;
>num : Symbol(num, Decl(contextuallyTypedParametersWithInitializers2.ts, 15, 13))

const test2: (arg: 1 | 2) => void = (arg = num) => {};
>test2 : Symbol(test2, Decl(contextuallyTypedParametersWithInitializers2.ts, 16, 5))
>arg : Symbol(arg, Decl(contextuallyTypedParametersWithInitializers2.ts, 16, 14))
>arg : Symbol(arg, Decl(contextuallyTypedParametersWithInitializers2.ts, 16, 37))
>num : Symbol(num, Decl(contextuallyTypedParametersWithInitializers2.ts, 15, 13))

const test3: (arg: number) => void = (arg = 1) => {};
>test3 : Symbol(test3, Decl(contextuallyTypedParametersWithInitializers2.ts, 18, 5))
>arg : Symbol(arg, Decl(contextuallyTypedParametersWithInitializers2.ts, 18, 14))
>arg : Symbol(arg, Decl(contextuallyTypedParametersWithInitializers2.ts, 18, 38))

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//// [tests/cases/compiler/contextuallyTypedParametersWithInitializers2.ts] ////

=== contextuallyTypedParametersWithInitializers2.ts ===
declare function test1<
>test1 : <TContext, TMethods extends Record<string, (ctx: TContext, ...args: never[]) => unknown>>(context: TContext, methods: TMethods) => void

TContext,
TMethods extends Record<string, (ctx: TContext, ...args: never[]) => unknown>,
>ctx : TContext
>args : never[]

>(context: TContext, methods: TMethods): void;
>context : TContext
>methods : TMethods

test1(
>test1( { count: 0, }, { checkLimit: (ctx, max = 500) => {}, hasAccess: (ctx, user: { name: string }) => {}, },) : void
>test1 : <TContext, TMethods extends Record<string, (ctx: TContext, ...args: never[]) => unknown>>(context: TContext, methods: TMethods) => void
{
>{ count: 0, } : { count: number; }

count: 0,
>count : number
>0 : 0

},
{
>{ checkLimit: (ctx, max = 500) => {}, hasAccess: (ctx, user: { name: string }) => {}, } : { checkLimit: (ctx: { count: number; }, max?: number) => void; hasAccess: (ctx: { count: number; }, user: { name: string;}) => void; }

checkLimit: (ctx, max = 500) => {},
>checkLimit : (ctx: { count: number; }, max?: number) => void
>(ctx, max = 500) => {} : (ctx: { count: number; }, max?: number) => void
>ctx : { count: number; }
>max : number
>500 : 500

hasAccess: (ctx, user: { name: string }) => {},
>hasAccess : (ctx: { count: number; }, user: { name: string;}) => void
>(ctx, user: { name: string }) => {} : (ctx: { count: number; }, user: { name: string;}) => void
>ctx : { count: number; }
>user : { name: string; }
>name : string

},
);

declare const num: number;
>num : number

const test2: (arg: 1 | 2) => void = (arg = num) => {};
>test2 : (arg: 1 | 2) => void
>arg : 1 | 2
>(arg = num) => {} : (arg?: number) => void
>arg : number
>num : number

const test3: (arg: number) => void = (arg = 1) => {};
>test3 : (arg: number) => void
>arg : number
>(arg = 1) => {} : (arg?: number) => void
>arg : number
>1 : 1

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//// [tests/cases/compiler/contextuallyTypedParametersWithInitializers3.ts] ////

=== contextuallyTypedParametersWithInitializers3.ts ===
type CanvasDirection = "RIGHT" | "LEFT";
>CanvasDirection : Symbol(CanvasDirection, Decl(contextuallyTypedParametersWithInitializers3.ts, 0, 0))

interface GraphActions {
>GraphActions : Symbol(GraphActions, Decl(contextuallyTypedParametersWithInitializers3.ts, 0, 40))

setDirection: (direction: CanvasDirection) => void;
>setDirection : Symbol(GraphActions.setDirection, Decl(contextuallyTypedParametersWithInitializers3.ts, 2, 24))
>direction : Symbol(direction, Decl(contextuallyTypedParametersWithInitializers3.ts, 3, 17))
>CanvasDirection : Symbol(CanvasDirection, Decl(contextuallyTypedParametersWithInitializers3.ts, 0, 0))
}

export declare function create<T>(config: T): void;
>create : Symbol(create, Decl(contextuallyTypedParametersWithInitializers3.ts, 4, 1))
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers3.ts, 6, 31))
>config : Symbol(config, Decl(contextuallyTypedParametersWithInitializers3.ts, 6, 34))
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers3.ts, 6, 31))

declare function takesDirection(direction: CanvasDirection): void;
>takesDirection : Symbol(takesDirection, Decl(contextuallyTypedParametersWithInitializers3.ts, 6, 51))
>direction : Symbol(direction, Decl(contextuallyTypedParametersWithInitializers3.ts, 8, 32))
>CanvasDirection : Symbol(CanvasDirection, Decl(contextuallyTypedParametersWithInitializers3.ts, 0, 0))

create<GraphActions>({
>create : Symbol(create, Decl(contextuallyTypedParametersWithInitializers3.ts, 4, 1))
>GraphActions : Symbol(GraphActions, Decl(contextuallyTypedParametersWithInitializers3.ts, 0, 40))

setDirection: (direction = "RIGHT") => {
>setDirection : Symbol(setDirection, Decl(contextuallyTypedParametersWithInitializers3.ts, 10, 22))
>direction : Symbol(direction, Decl(contextuallyTypedParametersWithInitializers3.ts, 11, 17))

takesDirection(direction);
>takesDirection : Symbol(takesDirection, Decl(contextuallyTypedParametersWithInitializers3.ts, 6, 51))
>direction : Symbol(direction, Decl(contextuallyTypedParametersWithInitializers3.ts, 11, 17))

},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//// [tests/cases/compiler/contextuallyTypedParametersWithInitializers3.ts] ////

=== contextuallyTypedParametersWithInitializers3.ts ===
type CanvasDirection = "RIGHT" | "LEFT";
>CanvasDirection : "RIGHT" | "LEFT"

interface GraphActions {
setDirection: (direction: CanvasDirection) => void;
>setDirection : (direction: CanvasDirection) => void
>direction : CanvasDirection
}

export declare function create<T>(config: T): void;
>create : <T>(config: T) => void
>config : T

declare function takesDirection(direction: CanvasDirection): void;
>takesDirection : (direction: CanvasDirection) => void
>direction : CanvasDirection

create<GraphActions>({
>create<GraphActions>({ setDirection: (direction = "RIGHT") => { takesDirection(direction); },}) : void
>create : <T>(config: T) => void
>{ setDirection: (direction = "RIGHT") => { takesDirection(direction); },} : { setDirection: (direction?: CanvasDirection) => void; }

setDirection: (direction = "RIGHT") => {
>setDirection : (direction?: CanvasDirection) => void
>(direction = "RIGHT") => { takesDirection(direction); } : (direction?: CanvasDirection) => void
>direction : CanvasDirection
>"RIGHT" : "RIGHT"

takesDirection(direction);
>takesDirection(direction) : void
>takesDirection : (direction: CanvasDirection) => void
>direction : CanvasDirection

},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//// [tests/cases/compiler/contextuallyTypedParametersWithInitializers4.ts] ////

=== contextuallyTypedParametersWithInitializers4.ts ===
declare function test<
>test : Symbol(test, Decl(contextuallyTypedParametersWithInitializers4.ts, 0, 0))

TContext,
>TContext : Symbol(TContext, Decl(contextuallyTypedParametersWithInitializers4.ts, 0, 22))

TMethods extends Record<string, (ctx: TContext, ...args: (1 | 2)[]) => unknown>,
>TMethods : Symbol(TMethods, Decl(contextuallyTypedParametersWithInitializers4.ts, 1, 11))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>ctx : Symbol(ctx, Decl(contextuallyTypedParametersWithInitializers4.ts, 2, 35))
>TContext : Symbol(TContext, Decl(contextuallyTypedParametersWithInitializers4.ts, 0, 22))
>args : Symbol(args, Decl(contextuallyTypedParametersWithInitializers4.ts, 2, 49))

>(context: TContext, methods: TMethods): void;
>context : Symbol(context, Decl(contextuallyTypedParametersWithInitializers4.ts, 3, 2))
>TContext : Symbol(TContext, Decl(contextuallyTypedParametersWithInitializers4.ts, 0, 22))
>methods : Symbol(methods, Decl(contextuallyTypedParametersWithInitializers4.ts, 3, 20))
>TMethods : Symbol(TMethods, Decl(contextuallyTypedParametersWithInitializers4.ts, 1, 11))

test(
>test : Symbol(test, Decl(contextuallyTypedParametersWithInitializers4.ts, 0, 0))
{
count: 0,
>count : Symbol(count, Decl(contextuallyTypedParametersWithInitializers4.ts, 6, 3))

},
{
checkLimit: (ctx, max = 3) => {},
>checkLimit : Symbol(checkLimit, Decl(contextuallyTypedParametersWithInitializers4.ts, 9, 3))
>ctx : Symbol(ctx, Decl(contextuallyTypedParametersWithInitializers4.ts, 10, 17))
>max : Symbol(max, Decl(contextuallyTypedParametersWithInitializers4.ts, 10, 21))

},
);

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//// [tests/cases/compiler/contextuallyTypedParametersWithInitializers4.ts] ////

=== contextuallyTypedParametersWithInitializers4.ts ===
declare function test<
>test : <TContext, TMethods extends Record<string, (ctx: TContext, ...args: (1 | 2)[]) => unknown>>(context: TContext, methods: TMethods) => void

TContext,
TMethods extends Record<string, (ctx: TContext, ...args: (1 | 2)[]) => unknown>,
>ctx : TContext
>args : (1 | 2)[]

>(context: TContext, methods: TMethods): void;
>context : TContext
>methods : TMethods

test(
>test( { count: 0, }, { checkLimit: (ctx, max = 3) => {}, },) : void
>test : <TContext, TMethods extends Record<string, (ctx: TContext, ...args: (1 | 2)[]) => unknown>>(context: TContext, methods: TMethods) => void
{
>{ count: 0, } : { count: number; }

count: 0,
>count : number
>0 : 0

},
{
>{ checkLimit: (ctx, max = 3) => {}, } : { checkLimit: (ctx: { count: number; }, max?: number) => void; }

checkLimit: (ctx, max = 3) => {},
>checkLimit : (ctx: { count: number; }, max?: number) => void
>(ctx, max = 3) => {} : (ctx: { count: number; }, max?: number) => void
>ctx : { count: number; }
>max : number
>3 : 3

},
);

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// @strict: true
// @noEmit: true

declare function test1<
TContext,
TMethods extends Record<string, (ctx: TContext, ...args: never[]) => unknown>,
>(context: TContext, methods: TMethods): void;

test1(
{
count: 0,
},
{
checkLimit: (ctx, max = 500) => {},
hasAccess: (ctx, user: { name: string }) => {},
},
);

declare const num: number;
const test2: (arg: 1 | 2) => void = (arg = num) => {};

const test3: (arg: number) => void = (arg = 1) => {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// @strict: true
// @noEmit: true

type CanvasDirection = "RIGHT" | "LEFT";

interface GraphActions {
setDirection: (direction: CanvasDirection) => void;
}

export declare function create<T>(config: T): void;

declare function takesDirection(direction: CanvasDirection): void;

create<GraphActions>({
setDirection: (direction = "RIGHT") => {
takesDirection(direction);
},
});
Loading

0 comments on commit 993ffd7

Please sign in to comment.