GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Higher order function type inference by ahejlsberg · Pull Request #30215 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Mar 4, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

With this PR we enable inference of generic function type results for generic functions that operate on other generic functions. For example:

declare function pipe<A extends any[], B, C>(ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C;

declare function list<T>(a: T): T[];
declare function box<V>(x: V): { value: V };

const listBox = pipe(list, box);  // <T>(a: T) => { value: T[] }
const boxList = pipe(box, list);  // <V>(x: V) => { value: V }[]

const x1 = listBox(42);  // { value: number[] }
const x2 = boxList("hello");  // { value: string }[]

const flip = <A, B, C>(f: (a: A, b: B) => C) => (b: B, a: A) => f(a, b);
const zip = <T, U>(x: T, y: U): [T, U] => [x, y];
const flipped = flip(zip);  // <T, U>(b: U, a: T) => [T, U]

const t1 = flipped(10, "hello");  // [string, number]
const t2 = flipped(true, 0);  // [number, boolean]

Previously, listBox would have type (x: any) => { value: any[] } with an ensuing loss of type safety downstream. boxList and flipped would likewise have type any in place of type parameters.

When an argument expression in a function call is of a generic function type, the type parameters of that function type are propagated onto the result type of the call if:

  • the called function is a generic function that returns a function type with a single call signature,
  • that single call signature doesn't itself introduce type parameters, and
  • in the left-to-right processing of the function call arguments, no inferences have been made for any of the type parameters referenced in the contextual type for the argument expression.

For example, in the call

const f = pipe(list, box);

as the arguments are processed left-to-right, nothing has been inferred for A and B upon processing the list argument. Therefore, the type parameter T from list is propagated onto the result of pipe and inferences are made in terms of that type parameter, inferring T for A and T[] for B. The box argument is then processed as before (because inferences exist for B), using the contextual type T[] for V in the instantiation of <V>(x: V) => { value: V } to produce (x: T[]) => { value: T[] }. Effectively, type parameter propagation happens only when we would have otherwise inferred the constraints of the called function's type parameters (which typically is the dreaded {}).

The above algorithm is not a complete unification algorithm and it is by no means perfect. In particular, it only delivers the desired outcome when types flow from left to right. However, this has always been the case for type argument inference in TypeScript, and it has the highly desired attribute of working well as code is being typed.

Note that this PR doesn't change our behavior for contextually typing arrow functions. For example, in

const f = pipe(x => [x], box);  // (x: any) => { value: any[] }

we infer any (the constraint declared by the pipe function) as we did before. We'll infer a generic type only if the arrow function explicitly declares a type parameter:

const f = pipe(<U>(x: U) => [x], box);  // <U>(x: U) => { value: U[] }

When necessary, inferred type parameters are given unique names:

declare function pipe2<A, B, C, D>(ab: (a: A) => B, cd: (c: C) => D): (a: [A, C]) => [B, D];

const f1 = pipe2(list, box);  // <T, V>(a: [T, V]) => [T[], { value: V }]
const f2 = pipe2(box, list);  // <V, T>(a: [V, T]) => [{ value: V }, T[]]
const f3 = pipe2(list, list);  // <T, T1>(a: [T, T1]) => [T[], T1[]]

Above, we rename the second T to T1 in the last example.

Fixes #417.
Fixes #3038.
Fixes #9366.
Fixes #9949.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

j-oliveras, schrepfler, dragomirtitian, KrisJordan, bartosz-k, benlesh, yortus, falsandtru, jkup, danielyogel, and 241 more reacted with thumbs up emoji fvilante, KiaraGrouwstra, arturkulig, j-oliveras, zpdDG4gta8XKpMCd, xiaoxiangmoe, psxcode, HerringtonDarkholme, Kingwl, BANG88, and 18 more reacted with laugh emoji ksaldana1, punmechanic, dubzzz, giogonzo, pelotom, j-oliveras, schrepfler, cartant, dragomirtitian, KrisJordan, and 116 more reacted with hooray emoji j-oliveras, schrepfler, wrumsby, felixrieseberg, samiskin, KrisJordan, kristian-puccio, Birowsky, benlesh, jkup, and 114 more reacted with heart emoji Bnaya, nateabele, karol-majewski, NikChao, cartant, sinisterstumble, JozefFlakus, wthinkit, fvilante, KiaraGrouwstra, and 46 more reacted with rocket emoji psxcode, zry656565, Kingwl, j-oliveras, AbraaoAlves, vyorkin, paavohuhtala, 0ubbe, another-guy, prism4time, and 3 more reacted with eyes emoji
All reactions
  • 👍 251 reactions
  • 😄 28 reactions
  • 🎉 126 reactions
  • ❤️ 124 reactions
  • 🚀 56 reactions
  • 👀 13 reactions
Anders Hejlsberg (ahejlsberg) added 14 commits February 28, 2019 06:15
@ahejlsberg
Convert CheckMode to a flags style enum
b44bfaa
@ahejlsberg
Skip generic functions along with context sensitive arguments
23473e0
@ahejlsberg
Merge branch 'master' into deferGenericFunctionInference
db5ca33
@ahejlsberg
Minor fixes
35ebbec
@ahejlsberg
Defer calls to generic functions returning generic functions
91f8fc6
@ahejlsberg
Add tests
304e25c
@ahejlsberg
Accept new baselines
e7881a4
@ahejlsberg
Infer higher order function types when possible
c344ef3
@ahejlsberg
Accept new baselines
c58819e
@ahejlsberg
Improve scheme for creating unique type parameter names
919ade1
@ahejlsberg
Minor fix
549c684
@ahejlsberg
Add tests
22c934a
@ahejlsberg
Accept new baselines
5fe8ebb
@ahejlsberg
Fix lint error
bf326aa
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Mar 4, 2019
Various issues when trying to use pipe function #29904
Closed
@ahejlsberg
Anders Hejlsberg (ahejlsberg) requested review from Daniel Rosenwasser (DanielRosenwasser), Ryan Cavanaugh (RyanCavanaugh), Nathan Shively-Sanders (sandersn) and Wesley Wigham (weswigham) March 4, 2019 22:32
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Mar 4, 2019
Supporting generic type inference over the other higher-order functions #9366
Closed
@jack-williams

Jack Williams (jack-williams) commented Mar 4, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

Is this the correct behaviour?

function ap<A>(fn: (x: A) => A, x: A): () => A {
    return () => fn(x);
}
declare function id<A>(x: A): A
const w = ap(id, 10); // Argument of type '10' is not assignable to parameter of type 'A'. [2345]

I think the logic that floats type parameters out when the return value is a generic function might be too eager. In existing cases where {} would have been inferred it might not be because the application is sufficiently generic, rather inference didn't produce anything sensible but that still type-checked.

I haven't looked into this much, so apologies in advance if I've got something wrong.

Xayne reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Mar 4, 2019

Copy link
Copy Markdown
Member Author

Jack Williams (@jack-williams) The core issue here is the left to right processing of the arguments. Previously we'd get it wrong as well (in that we'd infer {} instead of number), but now what was a loss of type safety instead has become an error. Ideally we ought to first infer from arguments that are not generic functions and then process the generic functions afterwards, similar to what we do for contextually typed arrow functions and function expressions.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jack-williams

Jack Williams (jack-williams) commented Mar 5, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

Anders Hejlsberg (@ahejlsberg) Ah yes, that makes sense. I agree that it was previously 'wrong', but I disagree that it was a loss of type safety (in this example). The call is perfectly safe, it's just imprecise. Previously this code was fine:

const x = ap(id, 10)();
if (typeof x === "number" && x === 10) {
    const ten: 10 = x;
}

but it will now raise an error. Could this be a significant issue for existing code?

I'll just add the disclaimer that this is really cool stuff and I'm not trying to be pedantic or needlessly pick holes. I'd like to be able to contribute more but I really haven't got my head around the main inference machinery, so all I can really do is try out some examples.

Xayne reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@RyanCavanaugh

Ryan Cavanaugh (RyanCavanaugh) commented Mar 5, 2019

Copy link
Copy Markdown
Member

The safe/imprecise distinction is sort of lossy given certain operations that are considered well-defined in generics but illegal in concrete code, e.g.

function ap<A>(fn: (x: A) => A, x: A): (a: A) => boolean {
    return y => y === x;
}
declare function id<A>(x: A): A
let w = ap(id, 10)("s");

or by advantage of covariance when you didn't intend to

function ap<A>(fn: (x: A) => A, x: A[]): (a: A) => void {
  return a => x.push(a);
}
function id<A>(x: A): A { return x; }
const arr: number[] = [10];
ap(id, arr)("s");
// prints a string from a number array
console.log(arr[1]);
HerringtonDarkholme and silouone reacted with laugh emoji
All reactions
  • 😄 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jack-williams

Jack Williams (jack-williams) commented Mar 5, 2019

Copy link
Copy Markdown
Collaborator

I think being lossy in isolation is ok; it's when you try and synthesise information that you get into trouble. The first example is IMO completely fine. The conjunction of union types, and equality over generics makes comparisons like that possible.

function eq<A>(x: A, y: A): boolean {
    return x === y;
}
eq<number | string>(10, "s");
eq(10, "s"); // error

It's just the behaviour of type inference that rejects the second application because it's probably not what the user intended. I accept that my definition of 'fine' here comes from a very narrow point-of-view, and that being 'fine' and unintentional is generally unhelpful.

I agree that the second example is very much borderline, though stuff like that is going to happen with covariance because you end up synthesising information.

I'm not trying to defend the existing behaviour; I'm all in favour of being more explicit. My only concern would stem from the fact that the new behaviour is somewhat unforgiving in that it flags the error immediately at the callsite. Users that previously handled the {} type downstream (either safely, or not), will now have to fix upstream breaks. I have no idea whether this is a practical problem, though.

On a slightly different note I wonder if the new inference behaviour could come with some improved error messages. There is a long way to go until TypeScript reaches the cryptic level of Haskell, but with generics always comes confusion. In particular, I wonder in the case of:

const w = ap(id, 10);

The error message is Argument of type '10' is not assignable to parameter of type 'A'. I wonder if there is a way to mark the parameter A as rigid and when relating to that type, suggest that the user given an instantiation for the call, and say that inference flows left-to-right.

RyanCavanaugh and mkantor reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@KiaraGrouwstra

kiara (KiaraGrouwstra) commented Mar 5, 2019

Copy link
Copy Markdown
Contributor

Ika (@ikatyang) Aleksey-Bykov Renamed in favour of (@whitecolor) Herrington Darkholme (@HerringtonDarkholme) Aluan Haddad (@aluanhaddad) Chet Corcos (@ccorcos) Marin Marinov (@gcnew) andretshurotshka (@goodmind)

RyanCavanaugh and fvilante reacted with laugh emoji zpdDG4gta8XKpMCd, ikatyang, HerringtonDarkholme, ccorcos, gcnew, fvilante, and trusktr reacted with hooray emoji zpdDG4gta8XKpMCd, ikatyang, HerringtonDarkholme, LukeSheard, and fvilante reacted with heart emoji ikatyang, zpdDG4gta8XKpMCd, HerringtonDarkholme, and fvilante reacted with rocket emoji
All reactions
  • 😄 2 reactions
  • 🎉 7 reactions
  • ❤️ 5 reactions
  • 🚀 4 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@zpdDG4gta8XKpMCd

ZpdDG4gta (zpdDG4gta8XKpMCd) commented Mar 5, 2019

Copy link
Copy Markdown

best day ever! thank you so much Anders Hejlsberg (@ahejlsberg) and the team you made my day! i don't really know what to wish for now (except HKT's 😛 )

c69, dewey92, ddoronin, landonpoch, steida, ShanonJackson, chrisfls, jacobmadsen, unao, fvilante, and 2 more reacted with thumbs up emoji c69, steida, and fvilante reacted with laugh emoji
All reactions
  • 👍 12 reactions
  • 😄 3 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Anders Hejlsberg (ahejlsberg) added 2 commits March 5, 2019 06:09
@ahejlsberg
Consistently defer generic functions to second type inference pass
ad823da
@ahejlsberg
Accept new baselines
cde9444
@tp Timm Preetz (tp) mentioned this pull request Mar 5, 2019
Wrong type inferred for indirect call of generic function #29638
Closed
@weswigham

Wesley Wigham (weswigham) commented Aug 2, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

RefForwardingComponent declares its type parameters as static, but for a generic component, you need type parameters on the signature, ergo you make your component look like a ref forwarding component (and it will be compatible with one), but not actually inherit from the declared shape:

export const HeaderMenuItem: <E extends object = ReactAnchorAttr>(props: React.PropsWithChildren<HeaderMenuItemProps<E>>, ref: React.Ref<HTMLElement>): React.ReactElement;
kalbert312, jbr, kaykaul, and rickstaa reacted with thumbs up emoji
All reactions
  • 👍 4 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@kalbert312

Kyle Albert (kalbert312) commented Aug 5, 2019

Copy link
Copy Markdown

That works, but was hoping it was possible to do something with the interface so all future changes on that interface are propagated to my definition. Is it not possible?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@weswigham

Wesley Wigham (weswigham) commented Aug 5, 2019

Copy link
Copy Markdown
Member

Not really, no. An interface simply can't have free type variables in the location you need for a generic component.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jcalz Joe Calzaretta (jcalz) mentioned this pull request Sep 2, 2019
Ability to get generic type from typeof and infer #33185
Open
5 tasks done
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Oct 3, 2019
Higher order type inference doesn't work with overloads #33594
Open
@fatcerberus Bruce Pascoe (fatcerberus) mentioned this pull request Nov 25, 2019
Type error when using generic function result as argument, but not when assigned to temp variable first #35339
Closed
@bergerbo

bergerbo commented Nov 28, 2019

Copy link
Copy Markdown

Hello Anders Hejlsberg (@ahejlsberg) !
Thanks for your work it's of great help !

I'm facing a weird issue regarding higher order function type inferance and I'd like your insight.

I'm trying to type a compose function and can't get it to work,
it's a mere copy of your pipe function, only with the parameters in reverse order.
Not only does it not infer, it doesn't even compile !

Here's what I have

declare function compose4<A extends any[], B, C, D, E>(
    de: (d: D) => E,
    cd: (c: C) => D,
    bc: (b: B) => C,
    ab: (...args: A) => B,
): (...args: A) => E;

const id = <T>(t: T) => t

const compose4id = compose4(id, id, id, id)

The error is positionned on the second parameter

Argument of type '<T>(t: T) => T' is not assignable to parameter of type '(c: T1) => T'.
  Type 'T1' is not assignable to type 'T'.
    'T1' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.(2345)

On the other hand, the same function with parameters in piping order works fine :

declare function pipe4<A extends any[], B, C, D, E>(
    ab: (...args: A) => B,
    bc: (b: B) => C,
    cd: (c: C) => D,
    de: (d: D) => E,
): (...args: A) => E;

const pipe4id = pipe4(id, id, id, id)

Is this by design ? Do you think of any work around I might be able to use here ?

Thanks again for everything !

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bergerbo

bergerbo commented Nov 28, 2019

Copy link
Copy Markdown

My bad found the related issue : #31738

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@maraisr

Marais Rossouw (maraisr) commented Jan 8, 2020 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

RefForwardingComponent declares its type parameters as static, but for a generic component, you need type parameters on the signature, ergo you make your component look like a ref forwarding component (and it will be compatible with one), but not actually inherit from the declared shape:

export const HeaderMenuItem: <E extends object = ReactAnchorAttr>(props: React.PropsWithChildren<HeaderMenuItemProps<E>>, ref: React.Ref<HTMLElement>): React.ReactElement;

Further that, is there a reason why something this wouldnt work?

const Test: <PayloadType extends unknown, E = HTMLUListElement>(props: SuggestionProps<PayloadType> & RefAttributes<E>) => ReactElement
	= forwardRef((props: SuggestionProps<PayloadType>, ref: Ref<E>) => {
	return <h1>test</h1>;
});

cc Wesley Wigham (@weswigham)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@johnrom

John Rom (johnrom) commented Jan 20, 2020 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Marais Rossouw (@maraisr) PayloadType is not available in the right side of the assignment, it's only available within the type definition itself. You could also define it as a generic to your inner component, but then PayloadType1 will not be assignable to PayloadType2 because of this error:

const Test: <PayloadType extends unknown, E = HTMLUListElement>(
  props: PayloadType & React.RefAttributes<E>
) => React.ReactElement = React.forwardRef(
  <PayloadType extends unknown, E = HTMLUListElement>(props: PayloadType, ref: React.Ref<E>) => {
    return <h1>test</h1>;
  }
);
// ERROR: 'unknown' is assignable to the constraint of type 'PayloadType', but 'PayloadType' could be instantiated with a different subtype of constraint 'unknown'.

So right now, at least as far as I can tell, the answer is to add // @ts-ignore and hope the types never change. The reason being that the error is actually not applicable here as we are guaranteeing that PayloadType will not be instantiated with a different subtype. Using Test will then work exactly how you expect it to in TS.

It wouldn't be so bad if we could just ignore the specific error above, but sadly ts-ignore will ignore all issues. ref: #19139

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@johnrom John Rom (johnrom) mentioned this pull request Jan 21, 2020
Use React.forwardRef in <Formik> jaredpalmer/formik#2208
Open
@johnrom John Rom (johnrom) mentioned this pull request Jan 29, 2020
Try using forwardRef jaredpalmer/formik#2222
Open
@sandersn Nathan Shively-Sanders (sandersn) mentioned this pull request Feb 3, 2020
Attempt at implementing propagated inference for free type parameters (#9366) #24626
Closed
@2A5F Package (2A5F) mentioned this pull request Feb 17, 2020
First class type constructor #35816
Closed
5 tasks done
@ghost ghost mentioned this pull request Mar 2, 2020
Generic values #17574
Open
@tony

Tony Narlock (tony) commented May 4, 2020

Copy link
Copy Markdown

Hi there! What commit is this merged in? It's difficult to find in the issue since there's a lot of mentions.

Thanks

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@DanielRosenwasser

Daniel Rosenwasser (DanielRosenwasser) commented May 4, 2020

Copy link
Copy Markdown
Member

TypeScript 3.4: https://github.com/Microsoft/TypeScript/wiki/Roadmap#34-march-2019

tony and rickstaa reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@tony

Tony Narlock (tony) commented May 4, 2020

Copy link
Copy Markdown

Thank you Daniel Rosenwasser (@DanielRosenwasser)!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@sisp Sigurd Spieckermann (sisp) mentioned this pull request May 2, 2021
Generic models xaviergonz/mobx-keystone#239
Closed
@mayteio Harley Alexander (mayteio) mentioned this pull request Aug 6, 2021
Support component prop generics with forwardRef chakra-ui/chakra-ui#4528
Closed
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Jun 13, 2022
Inferring a generic should put the type variable in the inner scope when possible #49505
Open
5 tasks done
@mwstr

Meowster (mwstr) commented Nov 16, 2022

Copy link
Copy Markdown

Hello 👋. I'm trying to create a Provider class that stores a handler function and can be executed with some context:

interface ProviderHandler<A extends any[] = any[], R = any> {
  (this: RequestContext, ...args: A): R
}

type FlatPromise<T> = Promise<T extends Promise<infer E> ? E : T>

interface WrapCall {
  <T extends ProviderHandler>(provider: Provider<T>): T extends ProviderHandler<
    infer A,
    infer R
  >
    ? (...args: A) => FlatPromise<R>
    : never
}

interface RequestContext {
  /* ... */
  call: WrapCall
}

class Provider<T extends ProviderHandler = ProviderHandler> {
  handler: T

  constructor(options: { handler: T }) {
    this.handler = options.handler
  }
}

const context = { /* imagine it implemented */ } as RequestContext

Everything works great if handler is a simple function:

const p1 = new Provider({
    async handler() {
        return 5
    }
})

// r1 = number
const r1 = await context.call(p1)()

But it does not work with generic handlers:

const p2 = new Provider({
    async handler<T>(t: T) {
        return t
    }
})

// actual: r2 = unknown
// expected: r2 = string
const r2 = await context.call(p2)('text')

Playground link

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@wbt wbt mentioned this pull request Dec 12, 2022
MyGeneric<X> is sometimes not assignable to MyGeneric<Y> though X extends Y #51867
Closed
@MichaelMitchell-at michaelm (MichaelMitchell-at) mentioned this pull request Apr 30, 2023
Add type operators that can answer "given function type T, what is the type of its return value when arguments P are passed in?" #40179
Open
5 tasks done
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Jun 21, 2023
Generics are lost during Function.prototype.bind() and Function.prototype.call() #54707
Open
5 tasks done
@yangwooseong Yang Wooseong (Andrew) (yangwooseong) mentioned this pull request Jul 14, 2023
Fix wrong typing of TabAction prop channel-io/bezier-react#1497
Merged
5 of 9 tasks
@webvv webvv mentioned this pull request Feb 4, 2024
TypeScript cannot infer type arguments correctly when swapping function parameters. #57287
Closed
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Aug 9, 2024
Broken type inference for union of function and object #59573
Closed
@microsoft Microsoft (microsoft) locked as resolved and limited conversation to collaborators Oct 21, 2025
@typescript-bot TypeScript Bot (typescript-bot) assigned Anders Hejlsberg (ahejlsberg) Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@weswigham Wesley Wigham (weswigham) weswigham left review comments
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) RyanCavanaugh approved these changes
@sandersn Nathan Shively-Sanders (sandersn) Awaiting requested review from sandersn
@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) Awaiting requested review from DanielRosenwasser

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@ahejlsberg Anders Hejlsberg (ahejlsberg)

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

question: how can i create and return a generic function in TypeScript? Supporting generic type inference over the other higher-order functions Inference bug Higher order functions lose type parameters

Uh oh!

There was an error while loading. Please reload this page.

20 participants

@ahejlsberg @jack-williams @RyanCavanaugh @KiaraGrouwstra @zpdDG4gta8XKpMCd @typescript-bot @mweststrate @j-oliveras @maciejw @AnyhowStep @mivanovaxway @thupi @weswigham @kalbert312 @bergerbo @maraisr @johnrom @tony @DanielRosenwasser @mwstr
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.