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

Fixed an issue with not being able to use mapped type over union constraint as rest param #49947

14 changes: 11 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13861,7 +13861,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const typeVariable = getHomomorphicTypeVariable(type);
if (typeVariable && !type.declaration.nameType) {
const constraint = getConstraintOfTypeParameter(typeVariable);
if (constraint && isArrayType(constraint)) {
if (constraint && everyType(constraint, isArrayOrTupleType)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a way, this reverts the recent change from #52651 . That issue is now fixed by avoiding getting the apparent type of mapped types within getApparentTypeOfContextualType - you can see the change in the commit here

I think this is a better fix for that issue anyway, I just didn't figure it out earlier. Getting the "resolved" apparent type for mapped types with tuple constraint (and array) constraints is important for relationship checking. The mapped type that resolves~ to a tuple is usually "normalized" here, that allows tuple/array-oriented branches to relate things.

return instantiateType(type, prependTypeMapping(typeVariable, constraint, type.mapper));
}
}
Expand Down Expand Up @@ -21679,7 +21679,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return varianceResult;
}
}
else if (isReadonlyArrayType(target) ? isArrayOrTupleType(source) : isArrayType(target) && isTupleType(source) && !source.target.readonly) {
else if (isReadonlyArrayType(target) ? everyType(source, isArrayOrTupleType) : isArrayType(target) && everyType(source, t => isTupleType(t) && !t.target.readonly)) {
if (relation !== identityRelation) {
return isRelatedTo(getIndexTypeOfType(source, numberType) || anyType, getIndexTypeOfType(target, numberType) || anyType, RecursionFlags.Both, reportErrors);
}
Expand Down Expand Up @@ -29251,7 +29251,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
getContextualType(node, contextFlags);
const instantiatedType = instantiateContextualType(contextualType, node, contextFlags);
if (instantiatedType && !(contextFlags && contextFlags & ContextFlags.NoConstraints && instantiatedType.flags & TypeFlags.TypeVariable)) {
const apparentType = mapType(instantiatedType, getApparentType, /*noReductions*/ true);
const apparentType = mapType(
instantiatedType,
// When obtaining apparent type of *contextual* type we don't want to get apparent type of mapped types.
// That would evaluate mapped types with array or tuple type constraints too eagerly
// and thus it would prevent `getTypeOfPropertyOfContextualType` from obtaining per-position contextual type for elements of array literal expressions.
// Apparent type of other mapped types is already the mapped type itself so we can just avoid calling `getApparentType` here for all mapped types.
t => getObjectFlags(t) & ObjectFlags.Mapped ? t : getApparentType(t),
/*noReductions*/ true
);
return apparentType.flags & TypeFlags.Union && isObjectLiteralExpression(node) ? discriminateContextualTypeByObjectMembers(node, apparentType as UnionType) :
apparentType.flags & TypeFlags.Union && isJsxAttributes(node) ? discriminateContextualTypeByJSXAttributes(node, apparentType as UnionType) :
apparentType;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
=== tests/cases/compiler/mappedTypeTupleConstraintAssignability.ts ===
// https://github.com/microsoft/TypeScript/issues/53359#issuecomment-1475390594

type Writeable<T> = { -readonly [P in keyof T]: T[P] };
>Writeable : Symbol(Writeable, Decl(mappedTypeTupleConstraintAssignability.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 2, 15))
>P : Symbol(P, Decl(mappedTypeTupleConstraintAssignability.ts, 2, 33))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 2, 15))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 2, 15))
>P : Symbol(P, Decl(mappedTypeTupleConstraintAssignability.ts, 2, 33))

type EnumValues = [string, ...string[]];
>EnumValues : Symbol(EnumValues, Decl(mappedTypeTupleConstraintAssignability.ts, 2, 55))

type Values<T extends EnumValues> = { [k in T[number]]: k; };
>Values : Symbol(Values, Decl(mappedTypeTupleConstraintAssignability.ts, 3, 40))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 4, 12))
>EnumValues : Symbol(EnumValues, Decl(mappedTypeTupleConstraintAssignability.ts, 2, 55))
>k : Symbol(k, Decl(mappedTypeTupleConstraintAssignability.ts, 4, 39))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 4, 12))
>k : Symbol(k, Decl(mappedTypeTupleConstraintAssignability.ts, 4, 39))

declare class ZodEnum<T extends [string, ...string[]]> {
>ZodEnum : Symbol(ZodEnum, Decl(mappedTypeTupleConstraintAssignability.ts, 4, 61))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 6, 22))

get enum(): Values<T>
>enum : Symbol(ZodEnum.enum, Decl(mappedTypeTupleConstraintAssignability.ts, 6, 56))
>Values : Symbol(Values, Decl(mappedTypeTupleConstraintAssignability.ts, 3, 40))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 6, 22))
}

declare function createZodEnum<U extends string, T extends Readonly<[U, ...U[]]>>(values: T): ZodEnum<Writeable<T>>;
>createZodEnum : Symbol(createZodEnum, Decl(mappedTypeTupleConstraintAssignability.ts, 8, 1))
>U : Symbol(U, Decl(mappedTypeTupleConstraintAssignability.ts, 10, 31))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 10, 48))
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
>U : Symbol(U, Decl(mappedTypeTupleConstraintAssignability.ts, 10, 31))
>U : Symbol(U, Decl(mappedTypeTupleConstraintAssignability.ts, 10, 31))
>values : Symbol(values, Decl(mappedTypeTupleConstraintAssignability.ts, 10, 82))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 10, 48))
>ZodEnum : Symbol(ZodEnum, Decl(mappedTypeTupleConstraintAssignability.ts, 4, 61))
>Writeable : Symbol(Writeable, Decl(mappedTypeTupleConstraintAssignability.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 10, 48))

// https://github.com/microsoft/TypeScript/issues/53359#issuecomment-1475390607

type Maybe<T> = T | null | undefined;
>Maybe : Symbol(Maybe, Decl(mappedTypeTupleConstraintAssignability.ts, 10, 116))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 14, 11))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 14, 11))

type AnyTuple = [unknown, ...unknown[]];
>AnyTuple : Symbol(AnyTuple, Decl(mappedTypeTupleConstraintAssignability.ts, 14, 37))

type AnyObject = { [k: string]: any };
>AnyObject : Symbol(AnyObject, Decl(mappedTypeTupleConstraintAssignability.ts, 15, 40))
>k : Symbol(k, Decl(mappedTypeTupleConstraintAssignability.ts, 16, 20))

type Flags = "s" | "d" | "";
>Flags : Symbol(Flags, Decl(mappedTypeTupleConstraintAssignability.ts, 16, 38))

interface ISchema<T, C = any, F extends Flags = any, D = any> {
>ISchema : Symbol(ISchema, Decl(mappedTypeTupleConstraintAssignability.ts, 17, 28))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 19, 18))
>C : Symbol(C, Decl(mappedTypeTupleConstraintAssignability.ts, 19, 20))
>F : Symbol(F, Decl(mappedTypeTupleConstraintAssignability.ts, 19, 29))
>Flags : Symbol(Flags, Decl(mappedTypeTupleConstraintAssignability.ts, 16, 38))
>D : Symbol(D, Decl(mappedTypeTupleConstraintAssignability.ts, 19, 52))

__flags: F;
>__flags : Symbol(ISchema.__flags, Decl(mappedTypeTupleConstraintAssignability.ts, 19, 63))
>F : Symbol(F, Decl(mappedTypeTupleConstraintAssignability.ts, 19, 29))

__context: C;
>__context : Symbol(ISchema.__context, Decl(mappedTypeTupleConstraintAssignability.ts, 20, 13))
>C : Symbol(C, Decl(mappedTypeTupleConstraintAssignability.ts, 19, 20))

__outputType: T;
>__outputType : Symbol(ISchema.__outputType, Decl(mappedTypeTupleConstraintAssignability.ts, 21, 15))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 19, 18))

__default: D;
>__default : Symbol(ISchema.__default, Decl(mappedTypeTupleConstraintAssignability.ts, 22, 18))
>D : Symbol(D, Decl(mappedTypeTupleConstraintAssignability.ts, 19, 52))
}

declare class TupleSchema<
>TupleSchema : Symbol(TupleSchema, Decl(mappedTypeTupleConstraintAssignability.ts, 24, 1))

TType extends Maybe<AnyTuple> = AnyTuple | undefined,
>TType : Symbol(TType, Decl(mappedTypeTupleConstraintAssignability.ts, 26, 26))
>Maybe : Symbol(Maybe, Decl(mappedTypeTupleConstraintAssignability.ts, 10, 116))
>AnyTuple : Symbol(AnyTuple, Decl(mappedTypeTupleConstraintAssignability.ts, 14, 37))
>AnyTuple : Symbol(AnyTuple, Decl(mappedTypeTupleConstraintAssignability.ts, 14, 37))

TContext = AnyObject,
>TContext : Symbol(TContext, Decl(mappedTypeTupleConstraintAssignability.ts, 27, 55))
>AnyObject : Symbol(AnyObject, Decl(mappedTypeTupleConstraintAssignability.ts, 15, 40))

TDefault = undefined,
>TDefault : Symbol(TDefault, Decl(mappedTypeTupleConstraintAssignability.ts, 28, 23))

TFlags extends Flags = ""
>TFlags : Symbol(TFlags, Decl(mappedTypeTupleConstraintAssignability.ts, 29, 23))
>Flags : Symbol(Flags, Decl(mappedTypeTupleConstraintAssignability.ts, 16, 38))

> {
constructor(schemas: [ISchema<any>, ...ISchema<any>[]]);
>schemas : Symbol(schemas, Decl(mappedTypeTupleConstraintAssignability.ts, 32, 14))
>ISchema : Symbol(ISchema, Decl(mappedTypeTupleConstraintAssignability.ts, 17, 28))
>ISchema : Symbol(ISchema, Decl(mappedTypeTupleConstraintAssignability.ts, 17, 28))
}

export function create<T extends AnyTuple>(schemas: {
>create : Symbol(create, Decl(mappedTypeTupleConstraintAssignability.ts, 33, 1))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 35, 23))
>AnyTuple : Symbol(AnyTuple, Decl(mappedTypeTupleConstraintAssignability.ts, 14, 37))
>schemas : Symbol(schemas, Decl(mappedTypeTupleConstraintAssignability.ts, 35, 43))

[K in keyof T]: ISchema<T[K]>;
>K : Symbol(K, Decl(mappedTypeTupleConstraintAssignability.ts, 36, 3))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 35, 23))
>ISchema : Symbol(ISchema, Decl(mappedTypeTupleConstraintAssignability.ts, 17, 28))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 35, 23))
>K : Symbol(K, Decl(mappedTypeTupleConstraintAssignability.ts, 36, 3))

}) {
return new TupleSchema<T | undefined>(schemas);
>TupleSchema : Symbol(TupleSchema, Decl(mappedTypeTupleConstraintAssignability.ts, 24, 1))
>T : Symbol(T, Decl(mappedTypeTupleConstraintAssignability.ts, 35, 23))
>schemas : Symbol(schemas, Decl(mappedTypeTupleConstraintAssignability.ts, 35, 43))
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
=== tests/cases/compiler/mappedTypeTupleConstraintAssignability.ts ===
// https://github.com/microsoft/TypeScript/issues/53359#issuecomment-1475390594

type Writeable<T> = { -readonly [P in keyof T]: T[P] };
>Writeable : Writeable<T>

type EnumValues = [string, ...string[]];
>EnumValues : [string, ...string[]]

type Values<T extends EnumValues> = { [k in T[number]]: k; };
>Values : Values<T>

declare class ZodEnum<T extends [string, ...string[]]> {
>ZodEnum : ZodEnum<T>

get enum(): Values<T>
>enum : Values<T>
}

declare function createZodEnum<U extends string, T extends Readonly<[U, ...U[]]>>(values: T): ZodEnum<Writeable<T>>;
>createZodEnum : <U extends string, T extends readonly [U, ...U[]]>(values: T) => ZodEnum<Writeable<T>>
>values : T

// https://github.com/microsoft/TypeScript/issues/53359#issuecomment-1475390607

type Maybe<T> = T | null | undefined;
>Maybe : Maybe<T>

type AnyTuple = [unknown, ...unknown[]];
>AnyTuple : [unknown, ...unknown[]]

type AnyObject = { [k: string]: any };
>AnyObject : { [k: string]: any; }
>k : string

type Flags = "s" | "d" | "";
>Flags : "" | "s" | "d"

interface ISchema<T, C = any, F extends Flags = any, D = any> {
__flags: F;
>__flags : F

__context: C;
>__context : C

__outputType: T;
>__outputType : T

__default: D;
>__default : D
}

declare class TupleSchema<
>TupleSchema : TupleSchema<TType, TContext, TDefault, TFlags>

TType extends Maybe<AnyTuple> = AnyTuple | undefined,
TContext = AnyObject,
TDefault = undefined,
TFlags extends Flags = ""
> {
constructor(schemas: [ISchema<any>, ...ISchema<any>[]]);
>schemas : [ISchema<any, any, any, any>, ...ISchema<any, any, any, any>[]]
}

export function create<T extends AnyTuple>(schemas: {
>create : <T extends AnyTuple>(schemas: { [K in keyof T]: ISchema<T[K], any, any, any>; }) => TupleSchema<T | undefined, AnyObject, undefined, "">
>schemas : { [K in keyof T]: ISchema<T[K], any, any, any>; }

[K in keyof T]: ISchema<T[K]>;
}) {
return new TupleSchema<T | undefined>(schemas);
>new TupleSchema<T | undefined>(schemas) : TupleSchema<T | undefined, AnyObject, undefined, "">
>TupleSchema : typeof TupleSchema
>schemas : { [K in keyof T]: ISchema<T[K], any, any, any>; }
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
tests/cases/compiler/mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts(9,9): error TS2322: Type 'HomomorphicMappedType<T>' is not assignable to type 'any[]'.


==== tests/cases/compiler/mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts (1 errors) ====
type HomomorphicMappedType<T> = { [P in keyof T]: T[P] extends string ? boolean : null }

function test1<T extends [number] | [string]>(args: T) {
const arr: any[] = [] as HomomorphicMappedType<T>
const arr2: readonly any[] = [] as HomomorphicMappedType<T>
}

function test2<T extends [number] | readonly [string]>(args: T) {
const arr: any[] = [] as HomomorphicMappedType<T> // error
~~~
!!! error TS2322: Type 'HomomorphicMappedType<T>' is not assignable to type 'any[]'.
const arr2: readonly any[] = [] as HomomorphicMappedType<T>
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
=== tests/cases/compiler/mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts ===
type HomomorphicMappedType<T> = { [P in keyof T]: T[P] extends string ? boolean : null }
>HomomorphicMappedType : Symbol(HomomorphicMappedType, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 0, 27))
>P : Symbol(P, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 0, 35))
>T : Symbol(T, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 0, 27))
>T : Symbol(T, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 0, 27))
>P : Symbol(P, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 0, 35))

function test1<T extends [number] | [string]>(args: T) {
>test1 : Symbol(test1, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 0, 88))
>T : Symbol(T, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 2, 15))
>args : Symbol(args, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 2, 46))
>T : Symbol(T, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 2, 15))

const arr: any[] = [] as HomomorphicMappedType<T>
>arr : Symbol(arr, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 3, 7))
>HomomorphicMappedType : Symbol(HomomorphicMappedType, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 2, 15))

const arr2: readonly any[] = [] as HomomorphicMappedType<T>
>arr2 : Symbol(arr2, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 4, 7))
>HomomorphicMappedType : Symbol(HomomorphicMappedType, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 2, 15))
}

function test2<T extends [number] | readonly [string]>(args: T) {
>test2 : Symbol(test2, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 5, 1))
>T : Symbol(T, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 7, 15))
>args : Symbol(args, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 7, 55))
>T : Symbol(T, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 7, 15))

const arr: any[] = [] as HomomorphicMappedType<T> // error
>arr : Symbol(arr, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 8, 7))
>HomomorphicMappedType : Symbol(HomomorphicMappedType, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 7, 15))

const arr2: readonly any[] = [] as HomomorphicMappedType<T>
>arr2 : Symbol(arr2, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 9, 7))
>HomomorphicMappedType : Symbol(HomomorphicMappedType, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts, 7, 15))
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
=== tests/cases/compiler/mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts ===
type HomomorphicMappedType<T> = { [P in keyof T]: T[P] extends string ? boolean : null }
>HomomorphicMappedType : HomomorphicMappedType<T>

function test1<T extends [number] | [string]>(args: T) {
>test1 : <T extends [number] | [string]>(args: T) => void
>args : T

const arr: any[] = [] as HomomorphicMappedType<T>
>arr : any[]
>[] as HomomorphicMappedType<T> : HomomorphicMappedType<T>
>[] : []

const arr2: readonly any[] = [] as HomomorphicMappedType<T>
>arr2 : readonly any[]
>[] as HomomorphicMappedType<T> : HomomorphicMappedType<T>
>[] : []
}

function test2<T extends [number] | readonly [string]>(args: T) {
>test2 : <T extends [number] | readonly [string]>(args: T) => void
>args : T

const arr: any[] = [] as HomomorphicMappedType<T> // error
>arr : any[]
>[] as HomomorphicMappedType<T> : HomomorphicMappedType<T>
>[] : []

const arr2: readonly any[] = [] as HomomorphicMappedType<T>
>arr2 : readonly any[]
>[] as HomomorphicMappedType<T> : HomomorphicMappedType<T>
>[] : []
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
=== tests/cases/compiler/restParamUsingMappedTypeOverUnionConstraint.ts ===
// repro 29919#issuecomment-470948453

type HomomorphicMappedType<T> = { [P in keyof T]: T[P] extends string ? boolean : null }
>HomomorphicMappedType : Symbol(HomomorphicMappedType, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 0, 0))
>T : Symbol(T, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 2, 27))
>P : Symbol(P, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 2, 35))
>T : Symbol(T, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 2, 27))
>T : Symbol(T, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 2, 27))
>P : Symbol(P, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 2, 35))

declare function test<T extends [number] | [string]>(
>test : Symbol(test, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 2, 88))
>T : Symbol(T, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 4, 22))

args: T,
>args : Symbol(args, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 4, 53))
>T : Symbol(T, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 4, 22))

fn: (...args: HomomorphicMappedType<T>) => void
>fn : Symbol(fn, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 5, 10))
>args : Symbol(args, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 6, 7))
>HomomorphicMappedType : Symbol(HomomorphicMappedType, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 0, 0))
>T : Symbol(T, Decl(restParamUsingMappedTypeOverUnionConstraint.ts, 4, 22))

): void

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
=== tests/cases/compiler/restParamUsingMappedTypeOverUnionConstraint.ts ===
// repro 29919#issuecomment-470948453

type HomomorphicMappedType<T> = { [P in keyof T]: T[P] extends string ? boolean : null }
>HomomorphicMappedType : HomomorphicMappedType<T>

declare function test<T extends [number] | [string]>(
>test : <T extends [number] | [string]>(args: T, fn: (...args: HomomorphicMappedType<T>) => void) => void

args: T,
>args : T

fn: (...args: HomomorphicMappedType<T>) => void
>fn : (...args: HomomorphicMappedType<T>) => void
>args : HomomorphicMappedType<T>

): void

Loading