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

Mapping one discriminated union to another discriminated union does not work as expected #55316

Closed
wbolduc opened this issue Aug 8, 2023 · 3 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@wbolduc
Copy link

wbolduc commented Aug 8, 2023

πŸ”Ž Search Terms

discriminated union destructuring

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about discriminated unions (there are none)

⏯ Playground Link

https://www.typescriptlang.org/play?ts=5.2.0-beta#code/JYOwLgpgTgZghgYwgAgJIGcAyB7OATUAc2QG8AoASABtc8I8AuZeK9CAbkuwCMArJnrwgIwyAD7IAriDoxQ9TgF8yoSLEQoc+eqUo1tjZGCiSOXPgL7CwSsmACeABxQBZOPe4QtdPMgC8aFi0ROLI3gpkKuDQ8EiB3kQATLrUtPSJTCxsnBSClkIiodKy8ni2qjEaYWl4yeSpBhlGJma5FsiC1rYOzshuHl41yQEYCSCEyRLhtZxkCNgg6KLocAC2jlQoAQAUq1RM-Z7TAJQH7kdD-gB8KfOLoiTI+j4ANB18yIr+yHs5UBBgSRQECkJ5DJjPehvQSfJSzMgAegRAE1sJJkAg4CC8NgjAALYDoZDcSSiMAEomE5BYkDYewhLG+EAQehEyRsTboIkmcDAVYoeZ0OYLJbIFbrTbDZC7fZ9c6DAynOUDabDG71O6ix6QvDQj5fAK-Sj-QHAsEGZAAflBOqaxlMet4n2QTG14OYcFYEEdsLIinYQA

πŸ’» Code

`
interface IsLoading {
loaded: false;
obj: object | undefined;
}
interface Loaded {
loaded: true;
obj: object;
}
type MaybeLoaded = IsLoading | Loaded;

interface IsLoading2 {
loaded2: false;
obj: object | undefined;
}
interface Loaded2 {
loaded2: true;
obj: object;
}
type MaybeLoaded2 = IsLoading2 | Loaded2;

const sample = (ml: MaybeLoaded): MaybeLoaded2 => {
const { loaded, obj } = ml;
return { loaded2: loaded, obj };
};

//You can do this but this is annoying and needs useless runtime code
const sample2 = (ml: MaybeLoaded): MaybeLoaded2 => {
const { loaded, obj } = ml;
return loaded ? { loaded2: true, obj } : { loaded2: false, obj };
};
`

πŸ™ Actual behavior

There's an error mapping MaybeLoading to MaybeLoading2

πŸ™‚ Expected behavior

Both of these discriminated unions are structurally the same and should be "mappable" without having to cast using 'as' or writing needless runtime code

@jcalz
Copy link
Contributor

jcalz commented Aug 8, 2023

Essentially a duplicate of #30581

@wbolduc
Copy link
Author

wbolduc commented Aug 9, 2023

I see and the twitter post makes it easier to understand a little.

What is the best way to deal with this then? Is it just casting?

@jcalz
Copy link
Contributor

jcalz commented Aug 9, 2023

Presuming you mean type assertions then probably, yes.

If you really care about convincing the compiler to see what you're doing as safe, there's the approach at #47109 to switch to generic indexing. But that's not a natural refactoring for many cases, and because your discriminant type is not a valid key type it's even worse since we'd have to make up keys and you lose some inference you might want. It could look like this (using 0 and 1 as the dummy keys):

interface Obj {
	0: object | undefined
	1: object;
}
interface Bool {
	0: false;
	1: true;
}
type Keys = keyof Obj

type MaybeLoaded<K extends Keys = Keys> = { [P in K]: { loaded: Bool[P], obj: Obj[P] } }[K];
type MaybeLoaded2<K extends Keys = Keys> = { [P in K]: { loaded2: Bool[P], obj: Obj[P] } }[K];

const sample = <K extends Keys>(ml: MaybeLoaded<K>): MaybeLoaded2<K> => {
	return { loaded2: ml.loaded, obj: ml.obj }; // okay
}
const s: MaybeLoaded = { loaded: true, obj: {} }
const v = sample(s);

Playground link

That compiles with no error, but I doubt it's worth it. I'd probably just stick with type assertions in this case.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Aug 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

3 participants