Skip to content

Problem with generics and conditional types #34687

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
lffg opened this issue Oct 23, 2019 · 1 comment
Closed

Problem with generics and conditional types #34687

lffg opened this issue Oct 23, 2019 · 1 comment
Labels
Duplicate An existing issue was already created

Comments

@lffg
Copy link

lffg commented Oct 23, 2019

TypeScript Version: 3.7-beta

Search Terms: conditional types generic problem


Note: I'll say "reverse type" a lot in this issue. In such context, the reversed type of a number is string, and the reversed type of a string is a number.

I have the following code:

type Id = string | number;

type ObjectId<T extends Id> = {
  id: T;
};

type ValidObjectId = ObjectId<string> | ObjectId<number>

type ReverseId<T extends ValidObjectId> = T['id'] extends number ? string : number;

function encodeObjectId<T extends ObjectId<number>>(
  obj: T
): ReverseId<T> {
  const id = obj.id;

  // Why does the following does not accept a string type?
  // As it's the reverse of type number, because of the `ReverseId` type, it should be working.
  return id.toString();
}

Playground link.

In it, I have the type ReverseId, which converts the type of the id property. For example, if the given object id property has a number type, it will return string; and if the given object's id property has a string type, the number type will be returned.

And that actually works:

type Id = string | number;

type ObjectId<T extends Id> = {
  id: T;
};

type ValidObjectId = ObjectId<string> | ObjectId<number>

type ReverseId<T extends ValidObjectId> = T['id'] extends number ? string : number;

// The `t` constant has a `number` type, which is the "reverse" of the `string`
// type, used in the given `ObjectId`.
const t: ReverseId<ObjectId<string>> = 1;

Playground link.

The problem is: when the ObjectId comes from a generic, as we saw in the encodeObjectId function in the first example, the type does not work.

I think that this is a problem, because it work when the ObjectId type does not come from a generic, and don't work when this type comes from a generic.

@jack-williams
Copy link
Collaborator

Conditional types ignore the constraints on type parameters for reasons below:

// We check instantiations of the two
// types with type parameters mapped to their restrictive form, i.e. a form of the type parameter
// that has no constraint. This ensures that, for example, the type
//   type Foo<T extends { x: any }> = T extends { x: string } ? string : number
// doesn't immediately resolve to 'string' instead of being deferred.

and as a result ReverseID<T> does not eagerly reduce to string.

Duplicate of #31096

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Oct 30, 2019
@lffg lffg closed this as completed Oct 31, 2019
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

3 participants