Skip to content

Type tuples no longer inferred properly from rest arguments with mapped generic type tuples. #49556

Closed
@joelek

Description

@joelek

I recently updated the dependencies for a TypeScript-based project and unfortunately ended up with some unexpected breakage.

The TypeScript version was changed from 4.6 to 4.7 and type tuples are no longer inferred properly from rest arguments with mapped generic type tuples.

type MyMappedType<Primitive extends any> = {
	primitive: Primitive;
};

The project uses several helper types to map type tuples to other type tuples as shown below.

type TupleMapperOld<Tuple extends any[]> = {
	[Key in keyof Tuple]: Tuple[Key] extends Tuple[number] ? MyMappedType<Tuple[Key]> : never;
};

After updating to TypeScript 4.7 it is still possible to use the helper directly. TypeScript will correctly infer the type as expected.

// Type should be [MyMappedType<string>, MyMappedType<number>] in TypeScript 4.6 and TypeScript 4.7.
type MyMappedTupleOld = TupleMapperOld<[string, number]>;

However, type inference breaks when using the mapper to map the rest arguments of a generic function as shown below.

function extractPrimitivesOld<Tuple extends any[]>(...mappedTypes: TupleMapperOld<Tuple>): Tuple {
	return mappedTypes.map((mappedType) => mappedType.primitive) as Tuple;
}

// Inferred type should be [string, number] but becomes [unknown, unknown] in TypeScript 4.7.
const myPrimitiveTupleOld = extractPrimitivesOld({ primitive: "" }, { primitive: 0 });

The problem can be mitigated by explicitly specifying the generic type tuple argument. It can also be solved by removing the mapping constraints in the mapper as shown below.

type TupleMapperNew<Tuple extends any[]> = {
	[Key in keyof Tuple]: MyMappedType<Tuple[Key]>;
};

I decided to open this issue and let you decide if this is something worth investigating since I couldn't find any information about the difference in behaviour in the release notes.

🔎 Search Terms

tuple mapping
type inference
rest arguments
generics

🕗 Version & Regression Information

This changed between versions 4.6 and 4.7.

⏯ Playground Link

Playground 4.6.4

Playground 4.7.2

💻 Code

type MyMappedType<Primitive extends any> = {
	primitive: Primitive;
};

type TupleMapperOld<Tuple extends any[]> = {
	[Key in keyof Tuple]: Tuple[Key] extends Tuple[number] ? MyMappedType<Tuple[Key]> : never;
};

// Type should be [MyMappedType<string>, MyMappedType<number>] in TypeScript 4.6 and TypeScript 4.7.
type MyMappedTupleOld = TupleMapperOld<[string, number]>;

function extractPrimitivesOld<Tuple extends any[]>(...mappedTypes: TupleMapperOld<Tuple>): Tuple {
	return mappedTypes.map((mappedType) => mappedType.primitive) as Tuple;
}

// Inferred type should be [string, number] but becomes [unknown, unknown] in TypeScript 4.7.
const myPrimitiveTupleOld = extractPrimitivesOld({ primitive: "" }, { primitive: 0 });
//    ^?

type TupleMapperNew<Tuple extends any[]> = {
	[Key in keyof Tuple]: MyMappedType<Tuple[Key]>;
};

// Type should be [MyMappedType<string>, MyMappedType<number>] in TypeScript 4.6 and TypeScript 4.7.
type MyMappedTupleNew = TupleMapperNew<[string, number]>;

function extractPrimitivesNew<Tuple extends any[]>(...mappedTypes: TupleMapperNew<Tuple>): Tuple {
	return mappedTypes.map((mappedType) => mappedType.primitive) as Tuple;
}

// Inferred type should be [string, number] in TypeScript 4.6 and TypeScript 4.7.
const myPrimitiveTupleNew = extractPrimitivesNew({ primitive: "" }, { primitive: 0 });

🙁 Actual behavior

The type tuple is inferred as [unknown, unknown] in TypeScript 4.7.

🙂 Expected behavior

The type tuple is inferred as [string, number] in TypeScript 4.7.

Metadata

Metadata

Assignees

Labels

Fix AvailableA PR has been opened for this issueHas ReproThis issue has compiler-backed repros: https://aka.ms/ts-reprosNeeds InvestigationThis issue needs a team member to investigate its status.RescheduledThis issue was previously scheduled to an earlier milestone

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions