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

Using tagged unions to narrow generics #18215

Closed
icewind1991 opened this issue Sep 1, 2017 · 4 comments
Closed

Using tagged unions to narrow generics #18215

icewind1991 opened this issue Sep 1, 2017 · 4 comments
Labels
Duplicate An existing issue was already created

Comments

@icewind1991
Copy link

When using a tagged union as type argument to a generic function it would be nice if you can use the discriminant to narrow down the generic type to a single piece of the union.

Example:

interface TypeA {
	type: 'a';
	foo: number;
}

interface TypeB {
	type: 'b';
	bar: number;
}

type BothTypes = TypeA | TypeB;

function make<T extends BothTypes>(type: T['type']): T {
	switch (type) {
		case 'a':
			return {
				type: 'a',
				foo: 1
			};
		case 'b':
			return {
				type: 'b',
				bar: 1
			};
	}
}

const obj = make('a');
console.log(obj.foo);

currently typescript complains that both return statements are not assignable to T, and the returned object is the generic BothTypes instead of TypeA.

@akarzazi
Copy link

akarzazi commented Sep 1, 2017

This has nothing to do with union types, here is simplified version

interface TypeA {
    type: 'a';
    foo: number;
}

function make<T extends TypeA>(): T {
    return {
        type: 'a',
        foo: 1
    }
}

Error: Type '{ type: "a"; foo: number; }' is not assignable to type 'T'.

Because the parameter T may be a subtype of TypeA.

Try using instead this signature

function make<T extends BothTypes>(type: T['type']): BothTypes

@jcalz
Copy link
Contributor

jcalz commented Sep 2, 2017

Probably the desired workaround looks more like:

type MapTypes = {
  a: TypeA;
  b: TypeB;
}
function make<T extends BothTypes['type']>(type: T): MapTypes[T] {
  const t: BothTypes['type'] = type; // help control flow analysis
  switch (t) {
    case 'a':
      return {
        type: 'a',
        foo: 1
      };
    case 'b':
      return {
        type: 'b',
        bar: 1
      };
  }
  const never: never = t; // exhaustiveness check
  return never;
}

const obj = make('a');
console.log(obj.foo); // ok

@mhegazy
Copy link
Contributor

mhegazy commented Sep 5, 2017

seems like another manifestation of #17859.

@mhegazy mhegazy added the Duplicate An existing issue was already created label Sep 5, 2017
@mhegazy
Copy link
Contributor

mhegazy commented Sep 20, 2017

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@mhegazy mhegazy closed this as completed Sep 20, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants