-
Notifications
You must be signed in to change notification settings - Fork 12.4k
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
Support higher order inferences for constructor functions #31116
Conversation
@typescript-bot test this |
Heya @ahejlsberg, I've started to run the extended test suite on this PR at fcd6f52. You can monitor the build here. It should now contribute to this PR's status checks. |
@typescript-bot run dt |
Heya @ahejlsberg, I've started to run the parallelized Definitely Typed test suite on this PR at fcd6f52. You can monitor the build here. It should now contribute to this PR's status checks. |
@typescript-bot run dt |
Heya @ahejlsberg, I've started to run the parallelized Definitely Typed test suite on this PR at 4fe59dc. You can monitor the build here. It should now contribute to this PR's status checks. |
The |
@typescript-bot run dt |
Heya @ahejlsberg, I've started to run the parallelized Definitely Typed test suite on this PR at 3e79e8d. You can monitor the build here. It should now contribute to this PR's status checks. |
Hi @RyanCavanaugh @ahejlsberg, Please find below an adapted example from the one in this PR: declare class Point {
constructor(x: number, y: number)
readonly x: number
readonly y: number
}
declare class Bag<T> {
constructor(...args: T[])
contains(value: T): boolean
static foo: string
}
function asFunction<A extends any[], B>(cf: new (...args: A) => B) {
return (...args: A) => new cf(...args)
}
const newPoint = asFunction(Point) // (x: number, y: number) => Point
const newBag = asFunction(Bag) // <T>(...args: T[]) => Bag<T>
const p1 = new Point(10, 20) // Point
const p2 = newPoint(10, 20) // Point
const bag1 = new Bag(1, 2, 3) // Bag<number>
const bag2 = newBag('a', 'b', 'c') // Bag<string>
declare class Component<P> {
props: P
constructor(props: P)
}
interface ComponentClass<P> {
new (props: P): Component<P>
thisWillBreakGenericInference?: boolean // <--- this is breaking the type inference
}
declare function myHoc<P>(C: ComponentClass<P>): ComponentClass<P>
type GenericProps<T> = { foo: number; stuff: T }
declare class GenericComponent<T> extends Component<GenericProps<T>> {}
// Should be:
// const GenericComponent2: new <T>(props: GenericProps<T>) => Component<GenericProps<T>>
// Actually:
// const GenericComponent2: ComponentClass<GenericProps<unknown>>
const GenericComponent2 = myHoc(GenericComponent) Link to playground: The issue here is that React's ComponentClass interface has a few optional members defined. |
@guykr-stratoscale That's a known limitation. See discussion in #30650 (comment). |
This PR expands on #30215 to support higher order inferences for constructor functions and to permit function type arguments to higher order composition functions to have other members in addition to their single call signature.
Some examples:
The rules for determining when higher order inferences occur are revised as follows. Given a generic function
f
of type<…>(…, x: P, …) => R
, and an argument expressiona
of a generic function typeA
, in a call expressionf(…, a, …)
the type parameters ofA
are propagated onto the inferred result type of the function call if:P
is a function type with no type arguments, a single call or construct signature, and no other members, andR
is a function type with no type arguments, a single call or construct signature, and no other members, andA
is function type with type arguments and a single call or construct signature that matchesP
(note thatA
is permitted to have other members), andP
.Fixes #30650.