You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionisSomeAction(action: any): action is FSA<{message: string},void>{returntrue;}letaction={...};if(isSomeAction(action)){// `action.payload` may be undefined.console.log(action.payload.message);}
Since generic type can be anything, including undefined, I'm considering to remove the optional designation:
exportinterfaceFSA<Payload,Meta>{
...
payload: Payload;
...
}// now type guard worksletaction={...};if(isSomeAction(action)){console.log(action.payload.message);}
However, now the creation code fail:
functioncreateSomeAction(): FSA<undefined,undefined>{// error: `payload` and `meta` are requiredreturn{type: 'SOME_ACTION'};}
I understand that this is mixing two different contexts: Type of the value and type of the reference. But it would be nice to have a better solution to describe this situation?
functioncreateCounter<T>(x: number){return(t: T)=>{returnx+1}}constcount=createCounter<undefined>(1)count()// error, have to do `count(undefined)`
Reacted by Dmitrijs Minajevs, Michael Wilson, NN, vultix, Eric Wooley, Guilherme Pacheco, Jean-Christophe Delmas, Ludovit Hajzer, JorisMolnar, Denis Kustura and 9 more
There is nothing specific to generics here. for example:
declarefunctionf(a: string|undefined): void;f();// Not allowedf(undefined);// OK
Reacted by Homa Wong, Ludvík Prouza, Daniel M., Etienne Dldc, Carl, SlurpTheo, Charles Samborski, Michael Wilson, Jerko Steiner, Hector Maddock-Greene and 17 more
With generic defaults (#13487) landed, more people will encounter this issue. Should this be fixed?
i.e.:
exportinterfaceFSA<Payload=never>{payload: Payload;}exportclassSomeFSAimplementsFSA{// error. `payload` is missing}
Reacted by Erik Müller, Shayan Sayahi, Arseny Smoogly, Wade Peterson, Jason Kurian, Jarrod Mosen, Rhys van der Waerden, Charles Samborski, Ryan Smith, Rune Mehlsen and 9 more
Adding a little more fuel to the fire. We have our own Promise library (Microsoft/SyncTasks on GitHub) and are running across bugs that devs are introducing now that we've switched our codebase to strict null-compliant.
In a perfect world, we would like:
leta: Promise<number>;a.resolve();// not alloweda.resolve(4);// okletb: Promise<void>;b.resolve();// okb.resolve(4);// not okletc: Promise<number|undefined>;c.resolve()// not okc.resolve(4)// okc.resolve(undefined)// ok
But there isn't currently any SFINAE-style way to select valid methods based on T types. If T is void, then you shouldn't be passing anything to resolve, and the function will just get an undefined parameter passed.
Right now, the function signature is "resolve(param?: T)", but that lets you do:
SyncTasks.Resolved() -- which then has a resolved synctask with an undefined value, which will then explode anything downstream that doesn't allow an undefined value.
We're contemplating, for now, changing synctasks to require always taking a parameter, and just adding some void helpers to make the code less annoyingly verbose, but it's ugly compared to what we could do with method signature selectors.
Reacted by Wade Peterson, Alexey Morozov, Felix Becker, Hector Maddock-Greene and PanopticaRising
To be clear, I don't actually want fully SFINAE-able method selectors -- this is JS after all, you can't do that. But I think the real answer is that if you have a function parameter whose type is void, then it should auto-change the signature for that parameter to optional, and error if you pass anything other than undefined to it, if you DO pass a value.
Trivial change, but my preferred method after playing around with the suggestions in this thread:
classBase<T=any>{publicfoo(...arg: T[]){}publicbar(...arg: T[]){}publicqux(...arg: T[]){}}classAllRequiredextendsBase<string>{}classAllOptionalextendsBase{}constreq=newAllRequired();req.foo();// without an arg, this will throw an errorconstopt=newAllOptional();req.opt();// this is fine though
I'd also like to add that being able to tell Typescript to treat properties that can be undefined as optional would be useful for interop with Closure Compiler annotations, since their type system makes no distinction between the two and doesn't support any optional property syntax.
After 6 years since I raise this issue, I realized that what I really want is the other way around. Instead of "treating undefined as optional", I want it to be more expressive to say when the type is optional.
i.e. something like:
type FSA<Payload,Meta>=Equal<Payload,void>
? { ... }
: {payload: Payload}
There is a semantic difference between undefined and optional.
I don't want this suggestion makes them even more muddy.
There is a semantic difference between undefined and optional. I don't want this suggestion makes them even more muddy.
Unfortunately, every other issue asking for the behavior I'm describing has been marked as a duplicate of this one. Perhaps it would make sense to open a separate issue?
Unfortunately, every other issue asking for the behavior I'm describing has been marked as a duplicate of this one. Perhaps it would make sense to open a separate issue?
It is still the same issue. What I'm suggesting is that the solution should not "loosen" the TypeScript semantics. There should be an alternative to describe precisely what it is.
There are workarounds for some cases such as the one suggested here: #12400 (comment)
But it would be great if TS can provide a way to describe it out of the box without causing more confusion.
Additional food of thoughts: what is void and what is never? Does void maps to unit in Haskell and never maps to Void in Haskell?
Is there a way to use them in generics to precisely describe what we want here?
I disagree with treating undefined parameters as optional. If a function explicitly defines a parameter as potentially undefined, it should expect that argument to be provided.
functionf(n: number|undefined): boolean{return!!n;}f();// is an error, as it should be (incorrect number of arguments)
However, I would like to propose the opposite: treating optional parameters as non-undefined (unless explicitly specified). This would be aligned with the exactOptionalPropertyTypes compiler option. Perhaps add a new exactOptionalParameterTypes option?
functionf(n?: number): boolean{return!!n;}f();// allowedf(undefined);// should be an error! (but is not currently)functiong(n?: number|undefined): boolean{return!!n;}g();// allowedg(undefined);// allowed, but only because it satisfies the expected type
This aligns with object properties:
typeF={n?: number};constf1: F={};// allowedconstf2: F={n: undefined};// is an error (under exactOptionalPropertyTypes), as it should betypeG={n?: number|undefined};constg1: G={};// allowedconstg2: G={n: undefined};// allowed
So I just wanted to add a simple example with a few iterations I have been reflecting on.
We are creating a signal. A signal is simply a function that returns an object with a value property. The typing reflects the value of the signal, though you can pass in an initialValue as a param to the function.
Example 1
functionsignal<T>(initialValue: T){return{value: initialValue}}// OKconstcount=signal<number|undefined>(0)// OKconstcount=signal<number|undefined>(undefined)// NOT OKconstcount=signal<number|undefined>()// NOT OK, and void does not reflect the actual value of the signalconstcount=signal<number|void>()
Example 2
functionsignal<T>(): {value: T|undefined}functionsignal<T>(initialValue: T): {value: T}functionsignal<T>(initialValue?: T){return{value: initialValue}}// OKconstcount=signal<number>(0)// OK, though implicit through the function override that the signal// type becomes `number | undefined`constcount=signal<number>()
Example 3
functionsignal<T>(
...[initialValue]: (Textendsundefined ? []|[initialValue: T] : [initialValue: T])){return{value: initialValue}}// OKconstcount=signal<number|undefined>(0)// OK, though the param signature/errors of the initialValue is confusing to readconstcount=signal<number|undefined>()
So just wanted to share this to add to the discussion.
For me, as the typing reflects the actual value of the signal, I have chosen to go with Example 1 in this case. It arguably gives the most predictable result and developer experience.
Activity
unional commentedon Nov 21, 2016
I understand that this is mixing two different contexts: Type of the value and type of the reference. But it would be nice to have a better solution to describe this situation?
unional commentedon Nov 21, 2016
As a counter argument, it does make sense that the property should be not optional redux-utilities/flux-standard-action#45 (comment)
unional commentedon Jan 26, 2017
A simpler example:
refactor: improve typings (#45)
unional commentedon Feb 3, 2017
Ryan Cavanaugh (@RyanCavanaugh) Mohamed Hegazy (@mhegazy) do you have any feedback on this? 🌷
Can this be covered in default generic type when specifying the default type is undefined?
mhegazy commentedon Feb 3, 2017
There is nothing specific to generics here. for example:
[-]Accepting `undefined` for generic as optional[/-][+]Treeting `undefined` parameters as optional[/+][-]Treeting `undefined` parameters as optional[/-][+]Treating `undefined` parameters as optional[/+]unional commentedon Apr 22, 2017
With generic defaults (#13487) landed, more people will encounter this issue. Should this be fixed?
i.e.:
deregtd commentedon Nov 8, 2017
Adding a little more fuel to the fire. We have our own Promise library (Microsoft/SyncTasks on GitHub) and are running across bugs that devs are introducing now that we've switched our codebase to strict null-compliant.
In a perfect world, we would like:
But there isn't currently any SFINAE-style way to select valid methods based on T types. If T is void, then you shouldn't be passing anything to resolve, and the function will just get an undefined parameter passed.
Right now, the function signature is "resolve(param?: T)", but that lets you do:
SyncTasks.Resolved() -- which then has a resolved synctask with an undefined value, which will then explode anything downstream that doesn't allow an undefined value.
We're contemplating, for now, changing synctasks to require always taking a parameter, and just adding some void helpers to make the code less annoyingly verbose, but it's ugly compared to what we could do with method signature selectors.
deregtd commentedon Nov 9, 2017
To be clear, I don't actually want fully SFINAE-able method selectors -- this is JS after all, you can't do that. But I think the real answer is that if you have a function parameter whose type is void, then it should auto-change the signature for that parameter to optional, and error if you pass anything other than undefined to it, if you DO pass a value.
25 remaining items
DScheglov commentedon May 14, 2021
Hey, Seph Reed (@SephReed)
your workaround is a correct approach. But with minor update:
ericwooley commentedon May 16, 2021
There are lots of workarounds here, Maybe all that's really needed is some good documentation or utility types?
jacob-haller-roby commentedon Jul 1, 2021
Trivial change, but my preferred method after playing around with the suggestions in this thread:
freakzlike commentedon Aug 2, 2021
I tried to use
voidwith conditional types, but i still need to passundefinedas parameter.DScheglov commentedon Aug 2, 2021
freakzlike ,
for parameters it works in a little bit another way:
See in Playground
Also I suggest to make
ActionContextgeneric because it is the same for any possible Mutations:Paduado commentedon Nov 27, 2021
Solved it like this:
data-enabler commentedon Oct 11, 2022
I'd also like to add that being able to tell Typescript to treat properties that can be undefined as optional would be useful for interop with Closure Compiler annotations, since their type system makes no distinction between the two and doesn't support any optional property syntax.
unional commentedon Oct 12, 2022
After 6 years since I raise this issue, I realized that what I really want is the other way around. Instead of "treating
undefinedas optional", I want it to be more expressive to say when the type is optional.i.e. something like:
There is a semantic difference between
undefinedandoptional.I don't want this suggestion makes them even more muddy.
data-enabler commentedon Oct 12, 2022
Unfortunately, every other issue asking for the behavior I'm describing has been marked as a duplicate of this one. Perhaps it would make sense to open a separate issue?
unional commentedon Oct 12, 2022
It is still the same issue. What I'm suggesting is that the solution should not "loosen" the TypeScript semantics. There should be an alternative to describe precisely what it is.
There are workarounds for some cases such as the one suggested here: #12400 (comment)
But it would be great if TS can provide a way to describe it out of the box without causing more confusion.
Additional food of thoughts: what is
voidand what isnever? Doesvoidmaps tounitin Haskell andnevermaps toVoidin Haskell?Is there a way to use them in generics to precisely describe what we want here?
For those who are interested in type theory, I would recommend checking out Category Theory for Programmers by Bartosz Milewski
chharvey commentedon Jul 7, 2023
I disagree with treating
undefinedparameters as optional. If a function explicitly defines a parameter as potentially undefined, it should expect that argument to be provided.However, I would like to propose the opposite: treating optional parameters as non-undefined (unless explicitly specified). This would be aligned with the
exactOptionalPropertyTypescompiler option. Perhaps add a newexactOptionalParameterTypesoption?This aligns with object properties:
christianalfoni commentedon Jan 9, 2024
Hi there 😄
So I just wanted to add a simple example with a few iterations I have been reflecting on.
We are creating a signal. A signal is simply a function that returns an object with a
valueproperty. The typing reflects thevalueof the signal, though you can pass in aninitialValueas a param to the function.Example 1
Example 2
Example 3
So just wanted to share this to add to the discussion.
For me, as the typing reflects the actual value of the signal, I have chosen to go with Example 1 in this case. It arguably gives the most predictable result and developer experience.
phaux commentedon Sep 11, 2025
workaround:
usage:
zeorin commentedon Oct 3, 2025
Niki (@phaux) I adapted your approach to an arbitrary number of arguments:
Playground