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

Infer class type from keyof class type #37384

Closed
harrysolovay opened this issue Mar 13, 2020 · 4 comments
Closed

Infer class type from keyof class type #37384

harrysolovay opened this issue Mar 13, 2020 · 4 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@harrysolovay
Copy link

TypeScript Version: 3.8.2

Search Terms: infer, class, type, from, keyof, class, member, prop, deduce

View on TS Playground

Description:

// define a class
class MyClass {
  a?: string;
  b?: string;
}

// get the union type of the class keys
type MyFieldKey = keyof MyClass; // "a" | "b"

// create a utility type to get the class type from a given key type
type ClassType<FieldKey extends string | symbol> =
  FieldKey extends keyof infer ClassType ? ClassType : never;

// get the class type from the key type
type MyClassInferred = ClassType<MyFieldKey>; // the problem...

In this example, one might expect for MyClassInferred to have a signature of MyClass. Instead it has the following signature: { a: any } | { b: any }.

Is this by design?

Thanks!

Kind regards,

Harry

@jcalz
Copy link
Contributor

jcalz commented Mar 14, 2020

Stack Overflow might be a better place for this, since this does seem to be working as intended.

I'd say that K extends keyof infer T ? ... or the non-distributive equivalent [K] extends [keyof infer T] ? ... is unlikely to be a useful construct since all the compiler can reasonably infer for T is something like Record<K, any>. You've asked the compiler to infer a type which has a key a and/or b. It dutifully responds {a: any} | {b: any} (or {a: any, b: any} if non-distributive), which is valid.

It does not scan through all other types mentioned in the code to see if there's another type which it can use instead. That might make sense in a nominal type system where you could hope to enumerate all types, but in a structural one where types can exist without explicit declarations, it's a non-starter.

Hope that makes sense; good luck!

@harrysolovay
Copy link
Author

harrysolovay commented Mar 14, 2020

@jcalz thank you! Before you close the issue, I hope it's okay if I run a single use case by you, as I believe this nominal-ish feature is one of importance to the language.

Let's say you're using a validation library, which exports a decorator factory Validate. This factory accepts a function, whose first argument is the class and whose second is a specified member of that class (the field being decorated). We'd want to create a type for the factory-passed function. For now, let's call this type––the argument of the Validate decorator factory––Validator.

import {Validate, Validator} from "some-wild-validator"

class User {
  name: string;
  @Validate(validator)
  age: number;
}

const validator: Validator<User, "age"> = (user, age) => {
  // do something
}

Within the above Validator, we need two generic arguments in order to get correct inference on user and age.

If it were possible to use the infer type to work backwards from User["age"], we could make the following possible, while keeping the correct inference:

const validator: Validator<User["age"]> = (user, age) => {
  // do something
}

2 generic arguments -> 1 generic argument. Not a huge difference, but ice cool nonetheless.

I'd love to hear thoughts on this.

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Mar 17, 2020
@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.

@huan
Copy link

huan commented Nov 9, 2021

@harrysolovay Have you found any solution to infer the rest part of the interface?

I ran into this issue this week and still have not figuredout how to make it.

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

5 participants