Skip to content

exactOptionalPropertyTypes being inconsistent with T[keyof T] #47587

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
sarimarton opened this issue Jan 25, 2022 · 6 comments
Closed

exactOptionalPropertyTypes being inconsistent with T[keyof T] #47587

sarimarton opened this issue Jan 25, 2022 · 6 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@sarimarton
Copy link

Bug Report

πŸ”Ž Search Terms

exact optional property types

πŸ•— Version & Regression Information

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

type T = { a?: number; b: string }

// undefined value is correctly not possible
// @ts-expect-error
const x: T = { a: undefined }

// Should be number | string
type T2 = T[keyof T]

// This should be an error, but it's not
// @ts-expect-error
const x2: T2 = undefined

πŸ™ Actual behavior

T[keyof T] is string | number | undefined

πŸ™‚ Expected behavior

T[keyof T] is string | number

@fatcerberus
Copy link

fatcerberus commented Jan 25, 2022

T[K] is the type you get by reading from T with a key of type K. Having undefined in that type is therefore expected, regardless of exactOptionalPropertyTypes. If it wasn’t then

const x: T = {};
const x2: T2 = x.a;

would be a type error.

@sarimarton
Copy link
Author

T[K] is the type you get by reading from T with a key of type K. Having undefined in that type is therefore expected, regardless of exactOptionalPropertyTypes. If it wasn’t then

const x: T = {};
const x2: T2 = x.a;

would be a type error.

I see your point. That's a pity, because it means there's no safe way to type an object's possible keys' possible values. T[keyof T] was supposed to that, but any optional prop of T undermines it in a way which goes against exactOptionalPropertyTypes: optionality of a prop introduces an extra item in the value union. I understand that undefined is per se an expression of optionality, but exactOptionalPropertyTypes is supposed to nullify this rule.

@fatcerberus
Copy link

I understand that undefined is per se an expression of optionality, but exactOptionalPropertyTypes is supposed to nullify this rule.

Except it doesn't nullify the rule, because you can still read an undefined from the optional property. The only thing EOPT does is to prevent you from writing one.

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Jan 25, 2022
@RyanCavanaugh
Copy link
Member

This seems to be a misunderstanding of what EOPT means.

The type you want instead of T[keyof T] is this:

type NotUndefinedKey<T, K extends keyof T> = Pick<T, K> extends {} ? NotUndefined<T[K]> : T[K];
type GetWriteShape<T> = { [K in keyof T]-?: NotUndefinedKey<T, K> };
type TW = GetWriteShape<T>;
type M = TW[keyof TW];

@sarimarton
Copy link
Author

@RyanCavanaugh This code correctly removes undefined from optional properties, but it removes it too, when undefined is allowed:

type NotUndefined<T> = T extends undefined ? never : T;
type NotUndefinedKey<T, K extends keyof T> = Pick<T, K> extends {} ? NotUndefined<T[K]> : T[K];
type GetWriteShape<T> = { [K in keyof T]-?: NotUndefinedKey<T, K> };
type PossibleValues<T> = GetWriteShape<T>[keyof GetWriteShape<T>];

type T = { a: number, b: string, c: undefined }
type xxx = PossibleValues<T> // string | number
type yyy = T[keyof T] // string | number | undefined

@typescript-bot
Copy link
Collaborator

This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow or the TypeScript Discord community.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

4 participants