Skip to content

Problem with type intersection and typeof #33760

Closed

Description

TypeScript Version: 3.6.3

interface Type {
	obj: {
		a: number,
		b: number
	}[];
}

function f1() {
	return {
		obj: [
			{
				a: 1,
				b: 2,
				c: 3
			},
			{
				a: 11,
				b: 22,
				c: 33
			}
		]
	};
}

function f2() {
	const ret = f1();

	return ret as Type & typeof ret;
}

const t = f2();

function f3(obj: typeof t['obj'][number]): number {
	return obj.c;
}

const z = t.obj.map(o => f3(o));

I expect that the code works but the last line results in compilation error:

Argument of type '{ a: number; b: number; }' is not assignable to parameter of type '{ a: number; b: number; } & { a: number; b: number; c: number; }'.
  Property 'c' is missing in type '{ a: number; b: number; }' but required in type '{ a: number; b: number; c: number; }'.ts(2345)

The variable o implicitly gets the wrong { a: number; b: number; } type instead the correct { a: number; b: number; c: number; }.

Activity

  1. RyanCavanaugh commented on Oct 2, 2019

    @RyanCavanaugh
    Member

    It's basically nonsensical to intersect a tuple type with a variable-length array type; did you mean to write this instead?

    interface Type {
    	obj: { a: number, b: number }[];
    }

    The error is correct; if you took [] (a valid typeof ret) and "intersected" it with [{ a: 0, b: 0 }] (a valid Type["obj"], then the 0th element would lack a c property.

  2. asfernandes commented on Oct 2, 2019

    @asfernandes
    Author

    It's basically nonsensical to intersect a tuple type with a variable-length array type; did you mean to write this instead?

    Yes, sure. Typo to create small reproducable test case. I edited it.

    Note that type of t (viewing in vscode) is

    Type & {
        obj: {
            a: number;
            b: number;
            c: number;
        }[];
    }

    And all of this is valid:

    t.obj[0].c = 1;
    t.obj[1].c = 1;
    t.obj[2].c = 1;
    t.obj[3].c = 1;

    So t.obj.map(o => f3(o)) should be valid.

  3. asfernandes commented on Oct 2, 2019

    @asfernandes
    Author

    It's also interesting that it works if you change

    return ret as Type & typeof ret;

    to

    return ret as (typeof ret) & Type;

    But that does not change in all situations.

  4. RyanCavanaugh commented on Oct 17, 2019

    @RyanCavanaugh
    Member

    The issue here is that intersections of the form T[] & U[] aren't equivalent to (T & U)[]; this is a correct interpretation of the intersection operator because, for example, arr.push(T) is a valid operation on T[] but not a valid operation on (T & U)[]. Methods of intersections are exposed as naive overloads and order matters in this case because the first matching overload is picked for the purposes of contextual typing. It's not clear what better alternative would even exist without special-casing everything about arrays.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions