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

Extract not narrowing type union in mapped type passed as generic #57827

Open
mikeauclair opened this issue Mar 18, 2024 · 4 comments Β· May be fixed by #57838
Open

Extract not narrowing type union in mapped type passed as generic #57827

mikeauclair opened this issue Mar 18, 2024 · 4 comments Β· May be fixed by #57838
Assignees
Labels
Needs Investigation This issue needs a team member to investigate its status.

Comments

@mikeauclair
Copy link

πŸ”Ž Search Terms

Extract, type union

πŸ•— Version & Regression Information

  • This changed between versions 5.33 and 5.4

⏯ Playground Link

https://www.typescriptlang.org/play?ts=5.4.2#code/C4TwDgpgBAysBOBLAdgcwNIRAZwDwBUA+KAXigFEAPBAQwGNhcBrLAewDMoBvAWACgogqAG0AClBRQWIDlHxQa2OWIC6UCNQjIAJkuwIUqKAH4oogFxRkEAG4R4KyzWQh+AXwA0UfUjSF+-HSsyPpQAEYANqysYKRQBB6EABQAXpZwvhhYeEQAlKTEvAJQQSHAUOyWPoZxKe4BfCjA9uz00Ira3PyCAI4A7hBVBmj1fPwREOU06cNZOLgd-nxAA

πŸ’» Code

type StringKeys<T> = Extract<keyof {
    [P in keyof T as T[P] extends string ? P: never]: any
}, string>

const bloop = <T,>(z: StringKeys<T>) => {
  const f: string = z
}

interface asd {
  qwe: string
}

let a: StringKeys<asd>

πŸ™ Actual behavior

z is not assignable to string in the bloop function

πŸ™‚ Expected behavior

z should be assignable to string, because we extract string-extending keys

Additional information about the issue

This was working just fine on 5.3.3, and continues to work when the generic is concretized (hence the let at the bottom of the example)

@Andarist
Copy link
Contributor

Andarist commented Mar 18, 2024

Conditional types are tricky as they often stay deferred in generic contexts. This version could somewhat easily work though (and it doesn't):

type StringKeys<T> = keyof {
  [P in keyof T as T[P] extends string ? P & string : never]: any;
};

const bloop = <T,>(z: StringKeys<T>) => {
  const f: string = z; // errors but shouldnt
};

As a workaround, you can use this version:

type StringKeys<T> = keyof {
  [P in keyof T as T[P] extends string ? P : never]: any;
} &
  string;

const bloop = <T,>(z: StringKeys<T>) => {
  const f: string = z;
};

@mikeauclair
Copy link
Author

That workaround works in my real codebase, so I'll cut over to that approach for now - is this a matter of the intersection (instead of Extract) causing non-deferred evaluation?

@fatcerberus
Copy link

fatcerberus commented Mar 18, 2024

I would guess it’s just a matter of the compiler knowing that T & string is always assignable to string regardless of what T is (by definition, as A & B is the set of values assignable to both)

@RyanCavanaugh
Copy link
Member

Bisects to #56742

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Investigation This issue needs a team member to investigate its status.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants