Skip to content

Mapped Tuple Type, removing optional modifier always removes undefined from property type #31810

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

Closed
AnyhowStep opened this issue Jun 7, 2019 · 5 comments
Labels
Duplicate An existing issue was already created

Comments

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Jun 7, 2019

TypeScript Version: 3.4.1, 3.5.1

Search Terms: mapped tuple type, remove optional, removes undefined

Code

//strictNullChecks
function foo (_0 : number, _opt? : string) {

}
type RemoveOpt<T> = {
    [index in keyof T]-? : undefined|T[index]
}
/*
	Expected: type p = [number | undefined, string | undefined]
	Actual  : type p = [number | undefined, string]
*/
type p = RemoveOpt<Parameters<typeof foo>>;

Expected behavior:

type p = [number | undefined, string | undefined]

Actual behavior:

type p = [number | undefined, string]

Playground Link: Playground

Related Issues:

#31025 Related, but somewhat different. This is for mapped tuple types.

The workaround by @jcalz will not work for mapped tuple types


My original intent was to have [number, string|undefined] instead of [number, (string|undefined)?]

So, I tried to remove the optional modifier.

But it removed undefined as well.

So, I tried to add it back. But it refused to take it.

@AnyhowStep
Copy link
Contributor Author

AnyhowStep commented Jun 7, 2019

This workaround works,

//strictNullChecks
function foo (_0 : number, _opt? : string) {

}
type AddUndefined<T, OriginalT> = (
    {
        [index in keyof T] : (
            undefined extends OriginalT[Extract<index, keyof OriginalT>] ?
            undefined|T[index] :
            T[index]
        )
    }
);
type RemoveOpt<T> = (
    AddUndefined<
        {
            [index in keyof T]-? : T[index]
        },
        T
    >
)
//This is now [number, string|undefined]
type p = RemoveOpt<Parameters<typeof foo>>;

Playground

But I shouldn't have to use such a hack =/


I should call it RestoreUndefined or something like that.

And I should call it RemoveOptionalModifierAndPreserveUndefinedType or something

@fatcerberus
Copy link

fatcerberus commented Jun 7, 2019

I might be wrong about this but I think that in general, TS doesn't make a distinction between "optional thing" and "thing that might be undefined" other than for the purpose of checking for existence in object literals. Other than that they treated as equivalent.

@AnyhowStep
Copy link
Contributor Author

AnyhowStep commented Jun 7, 2019

Except, in the very first example, I would expect the -? to be applied first, and then the |undefined to be applied second.
So, the resultant type should be required but can be undefined.


Related to that somewhat is that I expect -? to only remove the optional modifier and not remove the |undefined type as well. Especially when I make it explicit that the type should include |undefined.

If I really wanted to remove undefined as well, I would just use Exclude<T[k], undefined>


It's not just object literals. Basically just any object type, if it is required but may be undefined, the property must exist.


But I guess one can't just break existing functionality...

@jcalz
Copy link
Contributor

jcalz commented Jun 7, 2019

This is a duplicate of #31025 unless the code that excludes undefined from property values is implemented separately for mapped tuples, right?

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jun 25, 2019
@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

5 participants