Skip to content

Preserve type parameter constraint in emitted mapped types while preserving their distributivity #54759

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

Merged
Merged
17 changes: 13 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6638,22 +6638,31 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const questionToken = type.declaration.questionToken ? factory.createToken(type.declaration.questionToken.kind) as QuestionToken | PlusToken | MinusToken : undefined;
let appropriateConstraintTypeNode: TypeNode;
let newTypeVariable: TypeReferenceNode | undefined;
let templateType = getTemplateTypeFromMappedType(type);
let typeParameter = getTypeParameterFromMappedType(type);
if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
// We have a { [P in keyof T]: X }
// We do this to ensure we retain the toplevel keyof-ness of the type which may be lost due to keyof distribution during `getConstraintTypeFromMappedType`
if (isHomomorphicMappedTypeWithNonHomomorphicInstantiation(type) && context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) {
const newParam = createTypeParameter(createSymbol(SymbolFlags.TypeParameter, "T" as __String));
const name = typeParameterToName(newParam, context);
const newConstraintParam = createTypeParameter(createSymbol(SymbolFlags.TypeParameter, "T" as __String));
const newTypeParam = createTypeParameter(createSymbol(SymbolFlags.TypeParameter, "K" as __String));
Copy link
Member

Choose a reason for hiding this comment

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

Ideally, this should reuse the symbol name of the existing key type, rather than always using K, just in case.

Copy link
Member

Choose a reason for hiding this comment

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

Also, it would definitely be better just to reuse the existing mapped type's key type parameter wholesale; I can't really think of why we'd need a unique copy here, and it's usually better to keep type identities minimal where possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've applied the first suggestion (reusing the symbol). I'm not exactly sure what's the expected result that I'm supposed to get here but I think I might be somewhat close using this extra diff:

git diff
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index dfbcd01413..33bbc22259 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -6642,7 +6642,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
                 let appropriateConstraintTypeNode: TypeNode;
                 let newTypeVariable: TypeReferenceNode | undefined;
                 let templateType = getTemplateTypeFromMappedType(type);
-                let typeParameter = getTypeParameterFromMappedType(type);
+                const typeParameter = getTypeParameterFromMappedType(type);
                 // If the mapped type isn't `keyof` constraint-declared, _but_ still has modifiers preserved, and its naive instantiation won't preserve modifiers because its constraint isn't `keyof` constrained, we have work to do
                 const needsModifierPreservingWrapper = !isMappedTypeWithKeyofConstraintDeclaration(type)
                     && !(getModifiersTypeFromMappedType(type).flags & TypeFlags.Unknown)
@@ -6653,14 +6653,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
                     // We do this to ensure we retain the toplevel keyof-ness of the type which may be lost due to keyof distribution during `getConstraintTypeFromMappedType`
                     if (isHomomorphicMappedTypeWithNonHomomorphicInstantiation(type) && context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) {
                         const newConstraintParam = createTypeParameter(createSymbol(SymbolFlags.TypeParameter, "T" as __String));
-                        const newTypeParam = createTypeParameter(typeParameter.symbol);
                         const name = typeParameterToName(newConstraintParam, context);
                         const target = type.target as MappedType;
-                        typeParameter = newTypeParam;
                         newTypeVariable = factory.createTypeReferenceNode(name);
                         templateType = instantiateType(
                             getTemplateTypeFromMappedType(target),
-                            makeArrayTypeMapper([getTypeParameterFromMappedType(target), getModifiersTypeFromMappedType(target)], [newTypeParam, newConstraintParam])
+                            makeArrayTypeMapper([getTypeParameterFromMappedType(target), getModifiersTypeFromMappedType(target)], [typeParameter, newConstraintParam])
                         );
                     }
                     appropriateConstraintTypeNode = factory.createTypeOperatorNode(SyntaxKind.KeyOfKeyword, newTypeVariable || typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context));

but the typeParameterToName in typeParameterToDeclarationWithConstraint still uses NodeBuilderFlags.GenerateNamesForShadowedTypeParams. Even if I save those flags on the side and generate this without this generation for shadowed type params then I get the same problem with templateTypeNode.

Am I even on the right track here? Or did I misunderstand your suggestion? It might be worthwhile to take a look at the diff here to see how it behaves with the latest patch that I pushed out.

Copy link
Member

Choose a reason for hiding this comment

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

Or did I misunderstand your suggestion?

No, that diff is it, it's just unfortunate that we're manufacturing new type identities basically to work around a shortcoming in scope tracking in the type parameter name serializer. I think the issue as-is that the type parameter isn't "visible" to the serializer because the context.enclosingDeclaration is too broad - maybe if, in addition to the patch you linked, you also temporarily override context.enclosingDeclaration with a fake scope, as in signatureToSignatureDeclarationHelper it'd work. In fact, mapped types are basically signature scopes (with only a type parameter) - that's almost certainly what's missing, and what manufacturing a new type is just a kludge covering.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To get it right:

  • I should stop reassigning typeParameter here (like in the short git diff posted above in the hidden details)
  • and I should reuse the fake scope strategy used by signatureToSignatureDeclarationHelper

The second thing... doesn't look completely straightforward so tbh this might take me a longer while especially since I have some more pressing reviews to address right now.

Copy link
Member

Choose a reason for hiding this comment

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

The second thing should be much, much easier now - just a call to enterNewScope.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I've used enterNewScope now but I'm not sure how to interpret the results. At times new type parameter named were introduced, at times some became reused. Maybe I should only enterNewScope in certain branches here?

Copy link
Member

Choose a reason for hiding this comment

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

Correct - you want to enterNewScope around the creation of the nodes that the type parameter of the mapped type should be in scope for - which is to say the nameType (if present), and the template.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That makes a lot of sense, thanks! I pushed out this change - the PR should be good to go now :)

const name = typeParameterToName(newConstraintParam, context);
const target = type.target as MappedType;
Copy link
Member

Choose a reason for hiding this comment

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

...This is probably more elegant than using a prependTypeMapping call on the existing mapper.

typeParameter = newTypeParam;
newTypeVariable = factory.createTypeReferenceNode(name);
templateType = instantiateType(
getTemplateTypeFromMappedType(target),
makeArrayTypeMapper([getTypeParameterFromMappedType(target), getModifiersTypeFromMappedType(target)], [newTypeParam, newConstraintParam])
);
}
appropriateConstraintTypeNode = factory.createTypeOperatorNode(SyntaxKind.KeyOfKeyword, newTypeVariable || typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context));
}
else {
appropriateConstraintTypeNode = typeToTypeNodeHelper(getConstraintTypeFromMappedType(type), context);
}
const typeParameterNode = typeParameterToDeclarationWithConstraint(getTypeParameterFromMappedType(type), context, appropriateConstraintTypeNode);
const typeParameterNode = typeParameterToDeclarationWithConstraint(typeParameter, context, appropriateConstraintTypeNode);
const nameTypeNode = type.declaration.nameType ? typeToTypeNodeHelper(getNameTypeFromMappedType(type)!, context) : undefined;
const templateTypeNode = typeToTypeNodeHelper(removeMissingType(getTemplateTypeFromMappedType(type), !!(getMappedTypeModifiers(type) & MappedTypeModifiers.IncludeOptional)), context);
const templateTypeNode = typeToTypeNodeHelper(removeMissingType(templateType, !!(getMappedTypeModifiers(type) & MappedTypeModifiers.IncludeOptional)), context);
const mappedTypeNode = factory.createMappedTypeNode(readonlyToken, typeParameterNode, nameTypeNode, questionToken, templateTypeNode, /*members*/ undefined);
context.approximateLength += 10;
const result = setEmitFlags(mappedTypeNode, EmitFlags.SingleLine);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ declare const _default: {
fn: <T_1 extends {
x: T_1["x"] extends infer T extends {
[x: string]: (...params: unknown[]) => unknown;
} ? { [K in keyof T]: T_1["x"][K]; } : never;
} ? { [K in keyof T]: T[K]; } : never;
}>(sliceIndex: T_1) => T_1["x"] extends infer T_2 extends {
[x: string]: (...params: unknown[]) => unknown;
} ? { [K_1 in keyof T_2]: Parameters<T_1["x"][K_1]>; } : never;
} ? { [K_1 in keyof T_2]: Parameters<T_2[K_1]>; } : never;
};
};
export default _default;
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//// [tests/cases/compiler/declarationEmitMappedTypePreservesTypeParameterConstraint.ts] ////

//// [declarationEmitMappedTypePreservesTypeParameterConstraint.ts]
// repro from https://github.com/microsoft/TypeScript/issues/54560

declare type requiredKeys<T extends object> = {
[k in keyof T]: undefined extends T[k] ? never : k;
}[keyof T];

declare type addQuestionMarks<
T extends object,
R extends keyof T = requiredKeys<T>
> = Pick<Required<T>, R> & Partial<T>;

declare type identity<T> = T;

declare type flatten<T> = identity<{
[k in keyof T]: T[k];
}>;

export declare abstract class ZodType<Output = any> {
readonly _output: Output;
}

export declare class ZodLiteral<T> extends ZodType<T> {}

export declare type ZodTypeAny = ZodType<any>;

export declare type baseObjectOutputType<Shape extends ZodRawShape> = {
[k in keyof Shape]: Shape[k]["_output"];
};

export declare type objectOutputType<Shape extends ZodRawShape> = flatten<
addQuestionMarks<baseObjectOutputType<Shape>>
>;

export declare type ZodRawShape = {
[k: string]: ZodTypeAny;
};

export const buildSchema = <V extends string>(
version: V
): objectOutputType<{
version: ZodLiteral<V>;
}> => ({} as any);

// repro from https://github.com/microsoft/TypeScript/issues/55049

type evaluate<t> = { [k in keyof t]: t[k] } & unknown

export type entryOf<o> = evaluate<
{ [k in keyof o]-?: [k, o[k] & ({} | null)] }[o extends readonly unknown[]
? keyof o & number
: keyof o]
>

export type entriesOf<o extends object> = evaluate<entryOf<o>[]>

export const entriesOf = <o extends object>(o: o) =>
Object.entries(o) as entriesOf<o>


//// [declarationEmitMappedTypePreservesTypeParameterConstraint.js]
"use strict";
// repro from https://github.com/microsoft/TypeScript/issues/54560
Object.defineProperty(exports, "__esModule", { value: true });
exports.entriesOf = exports.buildSchema = void 0;
var buildSchema = function (version) { return ({}); };
exports.buildSchema = buildSchema;
var entriesOf = function (o) {
return Object.entries(o);
};
exports.entriesOf = entriesOf;


//// [declarationEmitMappedTypePreservesTypeParameterConstraint.d.ts]
declare type requiredKeys<T extends object> = {
[k in keyof T]: undefined extends T[k] ? never : k;
}[keyof T];
declare type addQuestionMarks<T extends object, R extends keyof T = requiredKeys<T>> = Pick<Required<T>, R> & Partial<T>;
declare type identity<T> = T;
declare type flatten<T> = identity<{
[k in keyof T]: T[k];
}>;
export declare abstract class ZodType<Output = any> {
readonly _output: Output;
}
export declare class ZodLiteral<T> extends ZodType<T> {
}
export declare type ZodTypeAny = ZodType<any>;
export declare type baseObjectOutputType<Shape extends ZodRawShape> = {
[k in keyof Shape]: Shape[k]["_output"];
};
export declare type objectOutputType<Shape extends ZodRawShape> = flatten<addQuestionMarks<baseObjectOutputType<Shape>>>;
export declare type ZodRawShape = {
[k: string]: ZodTypeAny;
};
export declare const buildSchema: <V extends string>(version: V) => addQuestionMarks<baseObjectOutputType<{
version: ZodLiteral<V>;
}>, undefined extends V ? never : "version"> extends infer T ? { [K in keyof T]: T[K]; } : never;
type evaluate<t> = {
[k in keyof t]: t[k];
} & unknown;
export type entryOf<o> = evaluate<{
[k in keyof o]-?: [k, o[k] & ({} | null)];
}[o extends readonly unknown[] ? keyof o & number : keyof o]>;
export type entriesOf<o extends object> = evaluate<entryOf<o>[]>;
export declare const entriesOf: <o extends object>(o: o) => ({ [k in keyof o]-?: [k, o[k] & ({} | null)]; }[o extends readonly unknown[] ? keyof o & number : keyof o] extends infer T ? { [K in keyof T]: T[K]; } : never)[];
export {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
//// [tests/cases/compiler/declarationEmitMappedTypePreservesTypeParameterConstraint.ts] ////

=== declarationEmitMappedTypePreservesTypeParameterConstraint.ts ===
// repro from https://github.com/microsoft/TypeScript/issues/54560

declare type requiredKeys<T extends object> = {
>requiredKeys : Symbol(requiredKeys, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 0, 0))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 2, 26))

[k in keyof T]: undefined extends T[k] ? never : k;
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 3, 3))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 2, 26))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 2, 26))
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 3, 3))
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 3, 3))

}[keyof T];
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 2, 26))

declare type addQuestionMarks<
>addQuestionMarks : Symbol(addQuestionMarks, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 4, 11))

T extends object,
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 6, 30))

R extends keyof T = requiredKeys<T>
>R : Symbol(R, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 7, 19))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 6, 30))
>requiredKeys : Symbol(requiredKeys, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 0, 0))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 6, 30))

> = Pick<Required<T>, R> & Partial<T>;
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
>Required : Symbol(Required, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 6, 30))
>R : Symbol(R, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 7, 19))
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 6, 30))

declare type identity<T> = T;
>identity : Symbol(identity, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 9, 38))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 11, 22))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 11, 22))

declare type flatten<T> = identity<{
>flatten : Symbol(flatten, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 11, 29))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 13, 21))
>identity : Symbol(identity, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 9, 38))

[k in keyof T]: T[k];
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 14, 3))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 13, 21))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 13, 21))
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 14, 3))

}>;

export declare abstract class ZodType<Output = any> {
>ZodType : Symbol(ZodType, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 15, 3))
>Output : Symbol(Output, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 17, 38))

readonly _output: Output;
>_output : Symbol(ZodType._output, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 17, 53))
>Output : Symbol(Output, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 17, 38))
}

export declare class ZodLiteral<T> extends ZodType<T> {}
>ZodLiteral : Symbol(ZodLiteral, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 19, 1))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 21, 32))
>ZodType : Symbol(ZodType, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 15, 3))
>T : Symbol(T, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 21, 32))

export declare type ZodTypeAny = ZodType<any>;
>ZodTypeAny : Symbol(ZodTypeAny, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 21, 56))
>ZodType : Symbol(ZodType, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 15, 3))

export declare type baseObjectOutputType<Shape extends ZodRawShape> = {
>baseObjectOutputType : Symbol(baseObjectOutputType, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 23, 46))
>Shape : Symbol(Shape, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 25, 41))
>ZodRawShape : Symbol(ZodRawShape, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 31, 2))

[k in keyof Shape]: Shape[k]["_output"];
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 26, 3))
>Shape : Symbol(Shape, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 25, 41))
>Shape : Symbol(Shape, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 25, 41))
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 26, 3))

};

export declare type objectOutputType<Shape extends ZodRawShape> = flatten<
>objectOutputType : Symbol(objectOutputType, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 27, 2))
>Shape : Symbol(Shape, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 29, 37))
>ZodRawShape : Symbol(ZodRawShape, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 31, 2))
>flatten : Symbol(flatten, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 11, 29))

addQuestionMarks<baseObjectOutputType<Shape>>
>addQuestionMarks : Symbol(addQuestionMarks, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 4, 11))
>baseObjectOutputType : Symbol(baseObjectOutputType, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 23, 46))
>Shape : Symbol(Shape, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 29, 37))

>;

export declare type ZodRawShape = {
>ZodRawShape : Symbol(ZodRawShape, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 31, 2))

[k: string]: ZodTypeAny;
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 34, 3))
>ZodTypeAny : Symbol(ZodTypeAny, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 21, 56))

};

export const buildSchema = <V extends string>(
>buildSchema : Symbol(buildSchema, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 37, 12))
>V : Symbol(V, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 37, 28))

version: V
>version : Symbol(version, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 37, 46))
>V : Symbol(V, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 37, 28))

): objectOutputType<{
>objectOutputType : Symbol(objectOutputType, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 27, 2))

version: ZodLiteral<V>;
>version : Symbol(version, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 39, 21))
>ZodLiteral : Symbol(ZodLiteral, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 19, 1))
>V : Symbol(V, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 37, 28))

}> => ({} as any);

// repro from https://github.com/microsoft/TypeScript/issues/55049

type evaluate<t> = { [k in keyof t]: t[k] } & unknown
>evaluate : Symbol(evaluate, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 41, 18))
>t : Symbol(t, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 45, 14))
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 45, 22))
>t : Symbol(t, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 45, 14))
>t : Symbol(t, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 45, 14))
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 45, 22))

export type entryOf<o> = evaluate<
>entryOf : Symbol(entryOf, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 45, 53))
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 47, 20))
>evaluate : Symbol(evaluate, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 41, 18))

{ [k in keyof o]-?: [k, o[k] & ({} | null)] }[o extends readonly unknown[]
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 48, 7))
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 47, 20))
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 48, 7))
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 47, 20))
>k : Symbol(k, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 48, 7))
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 47, 20))

? keyof o & number
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 47, 20))

: keyof o]
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 47, 20))

>

export type entriesOf<o extends object> = evaluate<entryOf<o>[]>
>entriesOf : Symbol(entriesOf, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 51, 1), Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 12))
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 53, 22))
>evaluate : Symbol(evaluate, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 41, 18))
>entryOf : Symbol(entryOf, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 45, 53))
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 53, 22))

export const entriesOf = <o extends object>(o: o) =>
>entriesOf : Symbol(entriesOf, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 51, 1), Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 12))
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 26), Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 44))
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 26), Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 44))
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 26), Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 44))

Object.entries(o) as entriesOf<o>
>Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --))
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 26), Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 44))
>entriesOf : Symbol(entriesOf, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 51, 1), Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 12))
>o : Symbol(o, Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 26), Decl(declarationEmitMappedTypePreservesTypeParameterConstraint.ts, 55, 44))

Loading