Skip to content

Better resolution for correlations in template literal types #57388

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
webholics opened this issue Feb 12, 2024 · 6 comments
Closed

Better resolution for correlations in template literal types #57388

webholics opened this issue Feb 12, 2024 · 6 comments
Labels
Duplicate An existing issue was already created

Comments

@webholics
Copy link

webholics commented Feb 12, 2024

πŸ”Ž Search Terms

"template literal types", "correlation"

πŸ•— Version & Regression Information

This is the behavior in every version I tried, and I reviewed the FAQ for entries about "template literal types"

⏯ Playground Link

https://www.typescriptlang.org/play?#code/KYOwrgtgBAKgngB2FA3gKCrRwCCUC8UARAC7Y5EA0GWSAQgcWfUWgL5prPIDSwcAWQCGCRukwBteEgB003AF0AXMSFEoAH2IAjIgG4aU7HOx1lxAMbqtRACb72BtBYD2IAM4koAc2Ak+cIwAPDBQwAAeJKC27rTIhPIAfAAU3CowlFAA1vzuKgHCCFIKAJQEiVAABgAkKNxsMrU5cO5slVBCsa4eJE7cUAAKLu7uAJbaADbAAbGEAEp+YABOIPJB3C4AZj5+ASHYiYkGQA

πŸ’» Code

enum Type {
  TypeA = "typeA",
  TypeB = "typeB"
}

type KeyMap = {
  [Type.TypeA]: "a" | "b";
  [Type.TypeB]: "c" | "d";
};

const getKey = <T extends Type = Type>(type: T, keys: KeyMap[T]) => `${type}.${keys}` as const;

type PossibleKeys = ReturnType<typeof getKey<Type>>;

πŸ™ Actual behavior

type PossibleKeys = "typeA.a" | "typeA.b" | "typeA.c" | "typeA.d" | "typeB.a" | "typeB.b" | "typeB.c" | "typeB.d"

πŸ™‚ Expected behavior

type PossibleKeys = "typeA.a" | "typeA.b" | "typeB.c" | "typeB.d"

Additional information about the issue

I know that the docs about Template Literal Types state that "For each interpolated position in the template literal, the unions are cross multiplied". But I am wondering whether we could to better in such cases or if there is an alternative way to implement this.

@snarbies
Copy link

The union type you're getting is correct. The way the function is typed, the following is a valid call, which would return "typeA.c":

getKey<Type>(Type.TypeA, "c");

Technically, all of "typeA.a" | "typeA.b" | "typeA.c" | "typeA.d" | "typeB.a" | "typeB.b" | "typeB.c" | "typeB.d" are possible return results.

@MartinJohns
Copy link
Contributor

Same deal as #57372.

@webholics
Copy link
Author

I understand that getKey<Type>(Type.TypeA, "c"); is a valid call.
So
a) it seems to be unrelated to template literal types but more a general issue of specifying oneof generics.
b) there seems to be now solution currently to type this as intended

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Feb 12, 2024
@rotu
Copy link

rotu commented Feb 12, 2024

One workaround (probably not the best) is to use mapped types:

const getKey = <T extends Type = Type>(type: T, keys: KeyMap[T]) => `${type}.${keys}` as keyof {[K in Type as `${K}.${KeyMap[K]}`]:null};

Another is to use a seemingly useless extends to force type expansion to distribute over T.

const getKey = (<T extends Type = Type,>(type: T, keys: KeyMap[T]) => `${type}.${keys}` as (T extends any ? `${T}.${KeyMap[T]}`: never))

@webholics
Copy link
Author

Thanks @rotu ! At least this does the job. Your second example is wild.

@typescript-bot
Copy link
Collaborator

This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Feb 15, 2024
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

6 participants