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

Assignment of T[K] := T[K] when it's a union is too strictly checked #38762

Closed
zen0wu opened this issue May 25, 2020 · 7 comments
Closed

Assignment of T[K] := T[K] when it's a union is too strictly checked #38762

zen0wu opened this issue May 25, 2020 · 7 comments
Labels
Duplicate An existing issue was already created

Comments

@zen0wu
Copy link

zen0wu commented May 25, 2020

TypeScript Version: 3.9.2 and nightly

Search Terms: generic keys

Code

type NullableTypes = {
	'num': number | undefined,
	'str': string | undefined
}

type NonNullableTypes = {
	[K in keyof NullableTypes]: NonNullable<NullableTypes[K]>
}

function assertDefined<K extends keyof NullableTypes>(b: NullableTypes[K]): NonNullableTypes[K] {
	if (b) {
		return b
	}
	throw new Error('undefined')
}

Expected behavior:
It should pass type checking. The code is perfectly fine since with whatever choice of K,
the target and the source of the assignment should have the exactly same type because undefined is stripped out from the source.

Actual behavior:
TS reports Type 'undefined' is not assignable to type 'never'.

I'm aware this is the consequence of #30769. Not sure what would be the best fix here, but intuitively even with the restricted behavior, assigning from X[K] to Y[K] seems to be valid, given

  • X and Y is a union of something
  • K is the same
  • K extends keyof X & K extends keyof Y
  • Y[K] is a subset of X[K] or exactly the same

The logic here is there's cases where we don't want X[K] becoming an intersection from a union, based on the source side of the assignment. Also it's very easy that X[K] become a never and user will get confused about it.

Playground Link:

Related Issues:
#32693, #31445

@zen0wu zen0wu changed the title Assignment of T[K] := T[K] is too strictly checked Assignment of T[K] := T[K] when it's a union is too strictly checked May 25, 2020
@zen0wu
Copy link
Author

zen0wu commented May 27, 2020

Giving more thoughts, the following rule might be a good approximation?

Assignment from type Source[K] to Target[K] can be allowed (without stricten the rule to become an intersection) if

  • K is a union
  • For each part U of the union K, Source[U] is assignable to Target[U]

Again, I might be missing some obvious things and would be happy to learn more about this problem.

@zen0wu
Copy link
Author

zen0wu commented May 27, 2020

I guess this would also relate to #31445, which is the canonical issue around this meta issue. Except for this one, my use case is more on the type level, meaning both Source/Target and K are types, not actual values. Not sure if they're exactly handled in the same way, if yes, at least we have another example for the use case.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jun 2, 2020
@RyanCavanaugh
Copy link
Member

Indeed, this is just an alternate form of #31445; any statements about the correctness of this function are predicated on the same unmutated utterance of b at both sites.

@zen0wu
Copy link
Author

zen0wu commented Jun 2, 2020

@RyanCavanaugh Any ETA for this issue? We’ve been stuck with 3.4.5 for almost a year because of this exactly problem

@RyanCavanaugh
Copy link
Member

@zen0wu // @ts-ignore ? The underlying issue is extremely tricky and not likely to have a designed solution any time soon.

@zen0wu
Copy link
Author

zen0wu commented Jun 2, 2020

@RyanCavanaugh Good to know about its complexity.

Unfortunately ts-ignore is not an option for us. We use this pattern very extensively, where we have type-level Map that maps from a literal to a type of data model we have. Something like below. It's like a very central piece of our code base.

It might also be worth to mention that, because we have many tables in this map, and this intersection := union thing is pretty complicated, hence the type checking on this thing is very very slow. A single callsite can take up to 10 seconds.

type Models = {
  table1: Table1Value,
  table2: Table2Value,
}

@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

3 participants