Skip to content
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

Include type parameter defaults in contextual typing #50994

Merged
merged 4 commits into from Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/compiler/checker.ts
Expand Up @@ -27534,7 +27534,7 @@ namespace ts {
const inferenceContext = getInferenceContext(node);
// If no inferences have been made, nothing is gained from instantiating as type parameters
// would just be replaced with their defaults similar to the apparent type.
Copy link
Contributor

Choose a reason for hiding this comment

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

is this part of the comment here still relevant? with the code change in this PR this now reads weirdly to me, or there is some additional context missing here - like what kind of default type parameters should be ignored and what kind of default parameters might be useful. Or perhaps this comment was never about defaults and it should mention base constraints?

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right, that should say constraints, not defaults.

if (inferenceContext && contextFlags! & ContextFlags.Signature && some(inferenceContext.inferences, hasInferenceCandidates)) {
if (inferenceContext && contextFlags! & ContextFlags.Signature && some(inferenceContext.inferences, hasInferenceCandidatesOrDefault)) {
// For contextual signatures we incorporate all inferences made so far, e.g. from return
Copy link
Contributor

Choose a reason for hiding this comment

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

I also have an additional fix touching this very line. Since this is being touched now, perhaps you could take a look if it looks any good? :p

https://github.com/microsoft/TypeScript/pull/48838/files#diff-d9ab6589e714c71e657f601cf30ff51dfc607fc98419bf72e04f6b0fa92cc4b8R27537

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not quite sure what that fix is trying to accomplish. Either way I don't think it is related to the issue being solved in this PR, so I think it's better to keep it separate.

// types as well as arguments to the left in a function call.
return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper);
Expand Down Expand Up @@ -35145,6 +35145,10 @@ namespace ts {
return !!(info.candidates || info.contraCandidates);
}

function hasInferenceCandidatesOrDefault(info: InferenceInfo) {
return !!(info.candidates || info.contraCandidates || hasTypeParameterDefault(info.typeParameter));
}

function hasOverlappingInferences(a: InferenceInfo[], b: InferenceInfo[]) {
for (let i = 0; i < a.length; i++) {
if (hasInferenceCandidates(a[i]) && hasInferenceCandidates(b[i])) {
Expand Down
21 changes: 21 additions & 0 deletions tests/baselines/reference/genericInferenceDefaultTypeParameter.js
@@ -0,0 +1,21 @@
//// [genericInferenceDefaultTypeParameter.ts]
// Repro from #50858

type Type = {
a: (e: string) => void;
b: (e: number) => void;
}

declare function f1<T extends keyof Type = "a">(props: Type[T]): void;

f1(event => { });
f1<"a">(event => { });
f1<"b">(event => { });


//// [genericInferenceDefaultTypeParameter.js]
"use strict";
// Repro from #50858
f1(function (event) { });
f1(function (event) { });
f1(function (event) { });
@@ -0,0 +1,35 @@
=== tests/cases/compiler/genericInferenceDefaultTypeParameter.ts ===
// Repro from #50858

type Type = {
>Type : Symbol(Type, Decl(genericInferenceDefaultTypeParameter.ts, 0, 0))

a: (e: string) => void;
>a : Symbol(a, Decl(genericInferenceDefaultTypeParameter.ts, 2, 13))
>e : Symbol(e, Decl(genericInferenceDefaultTypeParameter.ts, 3, 8))

b: (e: number) => void;
>b : Symbol(b, Decl(genericInferenceDefaultTypeParameter.ts, 3, 27))
>e : Symbol(e, Decl(genericInferenceDefaultTypeParameter.ts, 4, 8))
}

declare function f1<T extends keyof Type = "a">(props: Type[T]): void;
>f1 : Symbol(f1, Decl(genericInferenceDefaultTypeParameter.ts, 5, 1))
>T : Symbol(T, Decl(genericInferenceDefaultTypeParameter.ts, 7, 20))
>Type : Symbol(Type, Decl(genericInferenceDefaultTypeParameter.ts, 0, 0))
>props : Symbol(props, Decl(genericInferenceDefaultTypeParameter.ts, 7, 48))
>Type : Symbol(Type, Decl(genericInferenceDefaultTypeParameter.ts, 0, 0))
>T : Symbol(T, Decl(genericInferenceDefaultTypeParameter.ts, 7, 20))

f1(event => { });
>f1 : Symbol(f1, Decl(genericInferenceDefaultTypeParameter.ts, 5, 1))
>event : Symbol(event, Decl(genericInferenceDefaultTypeParameter.ts, 9, 3))

f1<"a">(event => { });
>f1 : Symbol(f1, Decl(genericInferenceDefaultTypeParameter.ts, 5, 1))
>event : Symbol(event, Decl(genericInferenceDefaultTypeParameter.ts, 10, 8))

f1<"b">(event => { });
>f1 : Symbol(f1, Decl(genericInferenceDefaultTypeParameter.ts, 5, 1))
>event : Symbol(event, Decl(genericInferenceDefaultTypeParameter.ts, 11, 8))

@@ -0,0 +1,37 @@
=== tests/cases/compiler/genericInferenceDefaultTypeParameter.ts ===
// Repro from #50858

type Type = {
>Type : { a: (e: string) => void; b: (e: number) => void; }

a: (e: string) => void;
>a : (e: string) => void
>e : string

b: (e: number) => void;
>b : (e: number) => void
>e : number
}

declare function f1<T extends keyof Type = "a">(props: Type[T]): void;
>f1 : <T extends keyof Type = "a">(props: Type[T]) => void
>props : Type[T]

f1(event => { });
>f1(event => { }) : void
>f1 : <T extends keyof Type = "a">(props: Type[T]) => void
>event => { } : (event: string) => void
>event : string

f1<"a">(event => { });
>f1<"a">(event => { }) : void
>f1 : <T extends keyof Type = "a">(props: Type[T]) => void
>event => { } : (event: string) => void
>event : string

f1<"b">(event => { });
>f1<"b">(event => { }) : void
>f1 : <T extends keyof Type = "a">(props: Type[T]) => void
>event => { } : (event: number) => void
>event : number

@@ -0,0 +1,29 @@
//// [genericInferenceDefaultTypeParameterJsxReact.tsx]
/// <reference path="/.lib/react16.d.ts" />

// Repro from #50858

import React, { ComponentPropsWithRef, ElementType, ReactNode } from 'react';

type ButtonBaseProps<T extends ElementType> = ComponentPropsWithRef<T> & { children?: ReactNode };

function Component<T extends ElementType = 'span'>(props: ButtonBaseProps<T>) {
return <></>;
}

const v1 = <Component onClick={e => e.preventDefault()} />;


//// [genericInferenceDefaultTypeParameterJsxReact.js]
"use strict";
/// <reference path="react16.d.ts" />
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
exports.__esModule = true;
// Repro from #50858
var react_1 = __importDefault(require("react"));
function Component(props) {
return react_1["default"].createElement(react_1["default"].Fragment, null);
}
var v1 = react_1["default"].createElement(Component, { onClick: function (e) { return e.preventDefault(); } });
@@ -0,0 +1,40 @@
=== tests/cases/compiler/genericInferenceDefaultTypeParameterJsxReact.tsx ===
/// <reference path="react16.d.ts" />

// Repro from #50858

import React, { ComponentPropsWithRef, ElementType, ReactNode } from 'react';
>React : Symbol(React, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 6))
>ComponentPropsWithRef : Symbol(ComponentPropsWithRef, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 15))
>ElementType : Symbol(ElementType, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 38))
>ReactNode : Symbol(ReactNode, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 51))

type ButtonBaseProps<T extends ElementType> = ComponentPropsWithRef<T> & { children?: ReactNode };
>ButtonBaseProps : Symbol(ButtonBaseProps, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 77))
>T : Symbol(T, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 6, 21))
>ElementType : Symbol(ElementType, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 38))
>ComponentPropsWithRef : Symbol(ComponentPropsWithRef, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 15))
>T : Symbol(T, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 6, 21))
>children : Symbol(children, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 6, 74))
>ReactNode : Symbol(ReactNode, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 51))

function Component<T extends ElementType = 'span'>(props: ButtonBaseProps<T>) {
>Component : Symbol(Component, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 6, 98))
>T : Symbol(T, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 8, 19))
>ElementType : Symbol(ElementType, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 38))
>props : Symbol(props, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 8, 51))
>ButtonBaseProps : Symbol(ButtonBaseProps, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 77))
>T : Symbol(T, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 8, 19))

return <></>;
}

const v1 = <Component onClick={e => e.preventDefault()} />;
>v1 : Symbol(v1, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 12, 5))
>Component : Symbol(Component, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 6, 98))
>onClick : Symbol(onClick, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 12, 21))
>e : Symbol(e, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 12, 31))
>e.preventDefault : Symbol(React.SyntheticEvent.preventDefault, Decl(react16.d.ts, 642, 31))
>e : Symbol(e, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 12, 31))
>preventDefault : Symbol(React.SyntheticEvent.preventDefault, Decl(react16.d.ts, 642, 31))

@@ -0,0 +1,35 @@
=== tests/cases/compiler/genericInferenceDefaultTypeParameterJsxReact.tsx ===
/// <reference path="react16.d.ts" />

// Repro from #50858

import React, { ComponentPropsWithRef, ElementType, ReactNode } from 'react';
>React : typeof React
>ComponentPropsWithRef : any
>ElementType : any
>ReactNode : any

type ButtonBaseProps<T extends ElementType> = ComponentPropsWithRef<T> & { children?: ReactNode };
>ButtonBaseProps : ButtonBaseProps<T>
>children : React.ReactNode

function Component<T extends ElementType = 'span'>(props: ButtonBaseProps<T>) {
>Component : <T extends React.ElementType<any> = "span">(props: ButtonBaseProps<T>) => JSX.Element
>props : ButtonBaseProps<T>

return <></>;
><></> : JSX.Element
}

const v1 = <Component onClick={e => e.preventDefault()} />;
>v1 : JSX.Element
><Component onClick={e => e.preventDefault()} /> : JSX.Element
>Component : <T extends React.ElementType<any> = "span">(props: ButtonBaseProps<T>) => JSX.Element
>onClick : (e: React.MouseEvent<HTMLSpanElement>) => void
>e => e.preventDefault() : (e: React.MouseEvent<HTMLSpanElement>) => void
>e : React.MouseEvent<HTMLSpanElement>
>e.preventDefault() : void
>e.preventDefault : () => void
>e : React.MouseEvent<HTMLSpanElement>
>preventDefault : () => void

14 changes: 14 additions & 0 deletions tests/cases/compiler/genericInferenceDefaultTypeParameter.ts
@@ -0,0 +1,14 @@
// @strict: true

// Repro from #50858

type Type = {
a: (e: string) => void;
b: (e: number) => void;
}

declare function f1<T extends keyof Type = "a">(props: Type[T]): void;

f1(event => { });
f1<"a">(event => { });
f1<"b">(event => { });
@@ -0,0 +1,17 @@
// @strict: true
// @esModuleInterop: true
// @jsx: react

/// <reference path="/.lib/react16.d.ts" />

// Repro from #50858

import React, { ComponentPropsWithRef, ElementType, ReactNode } from 'react';

type ButtonBaseProps<T extends ElementType> = ComponentPropsWithRef<T> & { children?: ReactNode };

function Component<T extends ElementType = 'span'>(props: ButtonBaseProps<T>) {
return <></>;
}

const v1 = <Component onClick={e => e.preventDefault()} />;