question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Distributed keyof over union leads to erroneous indexed access

See original GitHub issue

Bug Report

🕗 Version & Regression Information

This is the behavior in every version I tried

⏯ Playground Link

Playground link with relevant code

💻 Code

type KeyOf<T> = T extends unknown ? keyof T : never

type Wrong<T> = { [K in KeyOf<T>]: T[K] }

🙁 Actual behavior

It is accepted by the type checker, but it shouldn’t be allowed because T could be a union of objects with unrelated properties. The computed type doesn’t even have any sense:

type test= Wrong<{ prop0: string } | { prop1: number; prop2: boolean } | { prop3: string[] }>
//   { prop0: unknown; prop1: unknown; prop2: unknown; prop3: unknown; }

🙂 Expected behavior

K shouldn’t be allowed to index type T.

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:11 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
bentongxyzcommented, May 19, 2022

Hi @RyanCavanaugh, @jfet97, @MartinJohns,

I created a PR that seem to fix this error.

The problem (paste again below) …

type KeyOf<T> = T extends unknown ? keyof T : never;
type Wrong<T> = { [K in KeyOf<T>]: T[K] }
//                                 ^^^^ `K` should not be allowed to index `T`

… seems to be that in structuredTypeRelatedTo (checker.ts L19684-L19691), KeyOf<T> is found to be related to T, because keyof T inside T extends unknown ? keyof T : never returns IndexType.

It seems to be a legit operation if e.g. T extends **object** ? keyof T : never instead of T extends [some union]/ unknown ? keyof T : never.

So my working solution is to guard against return IndexType if the above conditions are met.

I place the logic at getTrueTypeFromConditionalType.

However, I am quite new to this and I am not quite sure:

  1. that getTrueTypeFromConditionalType is the right place to place the logic; and
  2. what is the right type to return in place of IndexType (I am returning UnknownType)

Please do give me a few pointers whether I am approaching this correctly.

Please do correct me if I am wrong!

Thank you for reviewing!

1reaction
bentongxyzcommented, May 9, 2022

Hi there, I’d like to take a shot on this issue!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Mapped Types should distribute over union types · Issue #28339
This might be a bug or an intentional consequence of the original design, but mapped types do not distribute over union types.
Read more >
Omitting a shared property from a union type of objects results ...
The easiest fix for this is to create a version of Omit that does distribute over unions by employing a distributive conditional type....
Read more >
Unionize and Objectify: A Trick for Applying Conditional Types ...
We've used a mapped type [k in keyof T] and an index operation [keyof T] to transform the object type into a union...
Read more >
Documentation - Advanced Types - TypeScript
Union types are useful for modeling situations when values can overlap in the types they can take on. What happens when we need...
Read more >
An overview of computing with types • Tackling TypeScript
23.5.1 The index type query operator keyof; 23.5.2 The indexed access operator ... @ts-expect-error: Property 'propB' does not exist on type 'Union'. arg....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found