-
Notifications
You must be signed in to change notification settings - Fork 13k
Preserve the homomorphism of inlined mapped types in declaration emit #48091
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5079,10 +5079,16 @@ namespace ts { | |
const readonlyToken = type.declaration.readonlyToken ? factory.createToken(type.declaration.readonlyToken.kind) as ReadonlyKeyword | PlusToken | MinusToken : undefined; | ||
const questionToken = type.declaration.questionToken ? factory.createToken(type.declaration.questionToken.kind) as QuestionToken | PlusToken | MinusToken : undefined; | ||
let appropriateConstraintTypeNode: TypeNode; | ||
let newTypeVariable: TypeReferenceNode | undefined; | ||
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` | ||
appropriateConstraintTypeNode = factory.createTypeOperatorNode(SyntaxKind.KeyOfKeyword, typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context)); | ||
if (!(getModifiersTypeFromMappedType(type).flags & TypeFlags.TypeParameter) && context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) { | ||
const newParam = createTypeParameter(createSymbol(SymbolFlags.TypeParameter, "T" as __String)); | ||
const name = typeParameterToName(newParam, context); | ||
newTypeVariable = factory.createTypeReferenceNode(name); | ||
} | ||
appropriateConstraintTypeNode = factory.createTypeOperatorNode(SyntaxKind.KeyOfKeyword, newTypeVariable || typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context)); | ||
} | ||
else { | ||
appropriateConstraintTypeNode = typeToTypeNodeHelper(getConstraintTypeFromMappedType(type), context); | ||
|
@@ -5092,7 +5098,19 @@ namespace ts { | |
const templateTypeNode = typeToTypeNodeHelper(removeMissingType(getTemplateTypeFromMappedType(type), !!(getMappedTypeModifiers(type) & MappedTypeModifiers.IncludeOptional)), context); | ||
const mappedTypeNode = factory.createMappedTypeNode(readonlyToken, typeParameterNode, nameTypeNode, questionToken, templateTypeNode, /*members*/ undefined); | ||
context.approximateLength += 10; | ||
return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine); | ||
const result = setEmitFlags(mappedTypeNode, EmitFlags.SingleLine); | ||
if (isMappedTypeWithKeyofConstraintDeclaration(type) && !(getModifiersTypeFromMappedType(type).flags & TypeFlags.TypeParameter) && context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it probably doesn't make sense to precalculate this predicate; on the other hand, it's confusing to read as-is and a name would help. In particular, I don't understand why the modifiers type needs to be not-a-type-parameter. Can you give an example? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the modifiers type is a type parameter, it'll preserve the homomorphism of the mapped type on its own - we don't need to substitute in a temporary type parameter. |
||
// homomorphic mapped type with a non-homomorphic naive inlining | ||
// wrap it with a conditional like `SomeModifiersType extends infer U ? {..the mapped type...} : never` to ensure the resulting | ||
// type stays homomorphic | ||
return factory.createConditionalTypeNode( | ||
typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context), | ||
factory.createInferTypeNode(factory.createTypeParameterDeclaration(factory.cloneNode(newTypeVariable!.typeName) as Identifier)), | ||
result, | ||
factory.createKeywordTypeNode(SyntaxKind.NeverKeyword) | ||
); | ||
} | ||
return result; | ||
} | ||
|
||
function createAnonymousTypeNode(type: ObjectType): TypeNode { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
//// [tests/cases/compiler/mappedTypeGenericInstantiationPreservesHomomorphism.ts] //// | ||
|
||
//// [internal.ts] | ||
export declare function usePrivateType<T extends unknown[]>(...args: T): PrivateMapped<T[any]>; | ||
|
||
type PrivateMapped<Obj> = {[K in keyof Obj]: Obj[K]}; | ||
|
||
//// [api.ts] | ||
import {usePrivateType} from './internal'; | ||
export const mappedUnionWithPrivateType = <T extends unknown[]>(...args: T) => usePrivateType(...args); | ||
|
||
|
||
//// [internal.js] | ||
"use strict"; | ||
exports.__esModule = true; | ||
//// [api.js] | ||
"use strict"; | ||
exports.__esModule = true; | ||
exports.mappedUnionWithPrivateType = void 0; | ||
var internal_1 = require("./internal"); | ||
var mappedUnionWithPrivateType = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
return internal_1.usePrivateType.apply(void 0, args); | ||
}; | ||
exports.mappedUnionWithPrivateType = mappedUnionWithPrivateType; | ||
|
||
|
||
//// [internal.d.ts] | ||
export declare function usePrivateType<T extends unknown[]>(...args: T): PrivateMapped<T[any]>; | ||
declare type PrivateMapped<Obj> = { | ||
[K in keyof Obj]: Obj[K]; | ||
}; | ||
export {}; | ||
//// [api.d.ts] | ||
export declare const mappedUnionWithPrivateType: <T extends unknown[]>(...args: T) => T[any] extends infer T_1 ? { [K in keyof T_1]: T[any][K]; } : never; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
=== tests/cases/compiler/internal.ts === | ||
export declare function usePrivateType<T extends unknown[]>(...args: T): PrivateMapped<T[any]>; | ||
>usePrivateType : Symbol(usePrivateType, Decl(internal.ts, 0, 0)) | ||
>T : Symbol(T, Decl(internal.ts, 0, 39)) | ||
>args : Symbol(args, Decl(internal.ts, 0, 60)) | ||
>T : Symbol(T, Decl(internal.ts, 0, 39)) | ||
>PrivateMapped : Symbol(PrivateMapped, Decl(internal.ts, 0, 95)) | ||
>T : Symbol(T, Decl(internal.ts, 0, 39)) | ||
|
||
type PrivateMapped<Obj> = {[K in keyof Obj]: Obj[K]}; | ||
>PrivateMapped : Symbol(PrivateMapped, Decl(internal.ts, 0, 95)) | ||
>Obj : Symbol(Obj, Decl(internal.ts, 2, 19)) | ||
>K : Symbol(K, Decl(internal.ts, 2, 28)) | ||
>Obj : Symbol(Obj, Decl(internal.ts, 2, 19)) | ||
>Obj : Symbol(Obj, Decl(internal.ts, 2, 19)) | ||
>K : Symbol(K, Decl(internal.ts, 2, 28)) | ||
|
||
=== tests/cases/compiler/api.ts === | ||
import {usePrivateType} from './internal'; | ||
>usePrivateType : Symbol(usePrivateType, Decl(api.ts, 0, 8)) | ||
|
||
export const mappedUnionWithPrivateType = <T extends unknown[]>(...args: T) => usePrivateType(...args); | ||
>mappedUnionWithPrivateType : Symbol(mappedUnionWithPrivateType, Decl(api.ts, 1, 12)) | ||
>T : Symbol(T, Decl(api.ts, 1, 43)) | ||
>args : Symbol(args, Decl(api.ts, 1, 64)) | ||
>T : Symbol(T, Decl(api.ts, 1, 43)) | ||
>usePrivateType : Symbol(usePrivateType, Decl(api.ts, 0, 8)) | ||
>args : Symbol(args, Decl(api.ts, 1, 64)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
=== tests/cases/compiler/internal.ts === | ||
export declare function usePrivateType<T extends unknown[]>(...args: T): PrivateMapped<T[any]>; | ||
>usePrivateType : <T extends unknown[]>(...args: T) => PrivateMapped<T[any]> | ||
>args : T | ||
|
||
type PrivateMapped<Obj> = {[K in keyof Obj]: Obj[K]}; | ||
>PrivateMapped : PrivateMapped<Obj> | ||
|
||
=== tests/cases/compiler/api.ts === | ||
import {usePrivateType} from './internal'; | ||
>usePrivateType : <T extends unknown[]>(...args: T) => { [K in keyof T[any]]: T[any][K]; } | ||
|
||
export const mappedUnionWithPrivateType = <T extends unknown[]>(...args: T) => usePrivateType(...args); | ||
>mappedUnionWithPrivateType : <T extends unknown[]>(...args: T) => { [K in keyof T[any]]: T[any][K]; } | ||
><T extends unknown[]>(...args: T) => usePrivateType(...args) : <T extends unknown[]>(...args: T) => { [K in keyof T[any]]: T[any][K]; } | ||
>args : T | ||
>usePrivateType(...args) : { [K in keyof T[any]]: T[any][K]; } | ||
>usePrivateType : <T extends unknown[]>(...args: T) => { [K in keyof T[any]]: T[any][K]; } | ||
>...args : unknown | ||
>args : T | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// @declaration: true | ||
// @filename: internal.ts | ||
export declare function usePrivateType<T extends unknown[]>(...args: T): PrivateMapped<T[any]>; | ||
|
||
type PrivateMapped<Obj> = {[K in keyof Obj]: Obj[K]}; | ||
|
||
// @filename: api.ts | ||
import {usePrivateType} from './internal'; | ||
export const mappedUnionWithPrivateType = <T extends unknown[]>(...args: T) => usePrivateType(...args); |
Uh oh!
There was an error while loading. Please reload this page.