Skip to content

Extend isEmptyAnonymousObjectType with support for non-generic mapped types #56974

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20849,7 +20849,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function isEmptyAnonymousObjectType(type: Type) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note that this is different from isEmptyObjectType and none of those reuse the other. Perhaps with this change, some sharing could be introduced. I tried that at first and a lot of tests failed - but I didn't bother digging into this right now.

I don't really know what's the intended use case of both. Some code comments here would be highly appreciated. I could add the. However, I'd need first to learn from the team about the different goals of those two.

return !!(getObjectFlags(type) & ObjectFlags.Anonymous && (
const objectFlags = getObjectFlags(type);
if (objectFlags & ObjectFlags.Mapped) {
return !isGenericMappedType(type) && !!(getConstraintTypeFromMappedType(type as MappedType).flags & TypeFlags.Never);
}
return !!(objectFlags & ObjectFlags.Anonymous && (
(type as ResolvedType).members && isEmptyResolvedType(type as ResolvedType) ||
type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral && getMembersOfSymbol(type.symbol).size === 0
));
Expand Down

This file was deleted.

18,432 changes: 18,432 additions & 0 deletions tests/baselines/reference/declarationEmitPrivatePromiseLikeInterface.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//// [tests/cases/compiler/emptyObjectMappedTypeWithNeverKey.ts] ////

=== emptyObjectMappedTypeWithNeverKey.ts ===
declare const u: unknown;
>u : Symbol(u, Decl(emptyObjectMappedTypeWithNeverKey.ts, 0, 13))

type A = { [K in never]: any };
>A : Symbol(A, Decl(emptyObjectMappedTypeWithNeverKey.ts, 0, 25))
>K : Symbol(K, Decl(emptyObjectMappedTypeWithNeverKey.ts, 2, 12))

const a: null | undefined | A = u;
>a : Symbol(a, Decl(emptyObjectMappedTypeWithNeverKey.ts, 3, 5))
>A : Symbol(A, Decl(emptyObjectMappedTypeWithNeverKey.ts, 0, 25))
>u : Symbol(u, Decl(emptyObjectMappedTypeWithNeverKey.ts, 0, 13))

type Point = { x: number; y: number };
>Point : Symbol(Point, Decl(emptyObjectMappedTypeWithNeverKey.ts, 3, 34))
>x : Symbol(x, Decl(emptyObjectMappedTypeWithNeverKey.ts, 5, 14))
>y : Symbol(y, Decl(emptyObjectMappedTypeWithNeverKey.ts, 5, 25))

declare function foo<T, K extends keyof T>(
>foo : Symbol(foo, Decl(emptyObjectMappedTypeWithNeverKey.ts, 5, 38))
>T : Symbol(T, Decl(emptyObjectMappedTypeWithNeverKey.ts, 6, 21))
>K : Symbol(K, Decl(emptyObjectMappedTypeWithNeverKey.ts, 6, 23))
>T : Symbol(T, Decl(emptyObjectMappedTypeWithNeverKey.ts, 6, 21))

obj: T,
>obj : Symbol(obj, Decl(emptyObjectMappedTypeWithNeverKey.ts, 6, 43))
>T : Symbol(T, Decl(emptyObjectMappedTypeWithNeverKey.ts, 6, 21))

keys: K[],
>keys : Symbol(keys, Decl(emptyObjectMappedTypeWithNeverKey.ts, 7, 9))
>K : Symbol(K, Decl(emptyObjectMappedTypeWithNeverKey.ts, 6, 23))

rest: Omit<T, K> | null | undefined,
>rest : Symbol(rest, Decl(emptyObjectMappedTypeWithNeverKey.ts, 8, 12))
>Omit : Symbol(Omit, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(emptyObjectMappedTypeWithNeverKey.ts, 6, 21))
>K : Symbol(K, Decl(emptyObjectMappedTypeWithNeverKey.ts, 6, 23))

): void;
const p: Point = { x: 0, y: 0 };
>p : Symbol(p, Decl(emptyObjectMappedTypeWithNeverKey.ts, 11, 5))
>Point : Symbol(Point, Decl(emptyObjectMappedTypeWithNeverKey.ts, 3, 34))
>x : Symbol(x, Decl(emptyObjectMappedTypeWithNeverKey.ts, 11, 18))
>y : Symbol(y, Decl(emptyObjectMappedTypeWithNeverKey.ts, 11, 24))

foo(p, ["x", "y"], u);
>foo : Symbol(foo, Decl(emptyObjectMappedTypeWithNeverKey.ts, 5, 38))
>p : Symbol(p, Decl(emptyObjectMappedTypeWithNeverKey.ts, 11, 5))
>u : Symbol(u, Decl(emptyObjectMappedTypeWithNeverKey.ts, 0, 13))

48 changes: 48 additions & 0 deletions tests/baselines/reference/emptyObjectMappedTypeWithNeverKey.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//// [tests/cases/compiler/emptyObjectMappedTypeWithNeverKey.ts] ////

=== emptyObjectMappedTypeWithNeverKey.ts ===
declare const u: unknown;
>u : unknown

type A = { [K in never]: any };
>A : {}

const a: null | undefined | A = u;
>a : A | null | undefined
>u : unknown

type Point = { x: number; y: number };
>Point : { x: number; y: number; }
>x : number
>y : number

declare function foo<T, K extends keyof T>(
>foo : <T, K extends keyof T>(obj: T, keys: K[], rest: Omit<T, K> | null | undefined) => void

obj: T,
>obj : T

keys: K[],
>keys : K[]

rest: Omit<T, K> | null | undefined,
>rest : Omit<T, K> | null | undefined

): void;
const p: Point = { x: 0, y: 0 };
>p : Point
>{ x: 0, y: 0 } : { x: number; y: number; }
>x : number
>0 : 0
>y : number
>0 : 0

foo(p, ["x", "y"], u);
>foo(p, ["x", "y"], u) : void
>foo : <T, K extends keyof T>(obj: T, keys: K[], rest: Omit<T, K> | null | undefined) => void
>p : Point
>["x", "y"] : ("x" | "y")[]
>"x" : "x"
>"y" : "y"
>u : unknown

6 changes: 3 additions & 3 deletions tests/baselines/reference/generatorYieldContextualType.types
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ function canPickStepContinue<T extends QuickPickStep>(
>_step : T

_state: PartialStepState,
>_state : PartialStepState<unknown>
>_state : { counter: number; confirm?: boolean | undefined; startingStep?: number | undefined; }

_selection: StepItemType<T> | Directive
>_selection : Directive | StepItemType<T>
Expand All @@ -220,7 +220,7 @@ function createPickStep<T extends QuickPickItem>(
}

function* showStep<
>showStep : <State extends Partial<unknown> & { counter: number; confirm?: boolean | undefined; startingStep?: number | undefined; } & { repo: any; }, Context extends { repos: any[]; title: string; status: any; }>(state: State, _context: Context) => StepResultGenerator<QuickPickItem>
>showStep : <State extends { counter: number; confirm?: boolean | undefined; startingStep?: number | undefined; } & { repo: any; }, Context extends { repos: any[]; title: string; status: any; }>(state: State, _context: Context) => StepResultGenerator<QuickPickItem>

State extends PartialStepState & { repo: any },
>repo : any
Expand Down Expand Up @@ -258,7 +258,7 @@ function* showStep<
return canPickStepContinue(step, state, selection)
>canPickStepContinue(step, state, selection) ? selection[0] : StepResult.Break : QuickPickItem | unique symbol
>canPickStepContinue(step, state, selection) : boolean
>canPickStepContinue : <T extends QuickPickStep<QuickPickItem>>(_step: T, _state: PartialStepState<unknown>, _selection: Directive | StepItemType<T>) => _selection is StepItemType<T>
>canPickStepContinue : <T extends QuickPickStep<QuickPickItem>>(_step: T, _state: { counter: number; confirm?: boolean | undefined; startingStep?: number | undefined; }, _selection: Directive | StepItemType<T>) => _selection is StepItemType<T>
>step : QuickPickStep<QuickPickItem>
>state : State
>selection : Directive | QuickPickItem[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ intersectionWithIndexSignatures.ts(17,1): error TS2322: Type '{ x: A; } & { y: B
Property 'y' is incompatible with index signature.
Property 'a' is missing in type 'B' but required in type 'A'.
intersectionWithIndexSignatures.ts(27,10): error TS2339: Property 'b' does not exist on type '{ a: string; }'.
intersectionWithIndexSignatures.ts(29,7): error TS2322: Type 's' is not assignable to type '{ [key: string]: { a: string; b: string; }; }'.
intersectionWithIndexSignatures.ts(29,7): error TS2322: Type 'Pick<{ [key: string]: { a: string; }; }, string | number>' is not assignable to type '{ [key: string]: { a: string; b: string; }; }'.
'string' index signatures are incompatible.
Property 'b' is missing in type '{ a: string; }' but required in type '{ a: string; b: string; }'.
intersectionWithIndexSignatures.ts(35,1): error TS2322: Type '{ a: string; } & { b: number; }' is not assignable to type '{ [key: string]: string; }'.
Expand Down Expand Up @@ -48,7 +48,7 @@ intersectionWithIndexSignatures.ts(35,1): error TS2322: Type '{ a: string; } & {

const d: { [key: string]: {a: string, b: string} } = q; // Error
~
!!! error TS2322: Type 's' is not assignable to type '{ [key: string]: { a: string; b: string; }; }'.
!!! error TS2322: Type 'Pick<{ [key: string]: { a: string; }; }, string | number>' is not assignable to type '{ [key: string]: { a: string; b: string; }; }'.
!!! error TS2322: 'string' index signatures are incompatible.
!!! error TS2322: Property 'b' is missing in type '{ a: string; }' but required in type '{ a: string; b: string; }'.
!!! related TS2728 intersectionWithIndexSignatures.ts:29:39: 'b' is declared here.
Expand Down
10 changes: 5 additions & 5 deletions tests/baselines/reference/intersectionWithIndexSignatures.types
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,19 @@ type constr<Source, Tgt> = { [K in keyof Source]: string } & Pick<Tgt, Exclude<k
>constr : constr<Source, Tgt>

type s = constr<{}, { [key: string]: { a: string } }>;
>s : {} & Pick<{ [key: string]: { a: string; }; }, string | number>
>s : Pick<{ [key: string]: { a: string; }; }, string | number>
>key : string
>a : string

declare const q: s;
>q : s
>q : Pick<{ [key: string]: { a: string; }; }, string | number>

q["asd"].a.substr(1);
>q["asd"].a.substr(1) : string
>q["asd"].a.substr : (from: number, length?: number | undefined) => string
>q["asd"].a : string
>q["asd"] : { a: string; }
>q : s
>q : Pick<{ [key: string]: { a: string; }; }, string | number>
>"asd" : "asd"
>a : string
>substr : (from: number, length?: number | undefined) => string
Expand All @@ -88,7 +88,7 @@ q["asd"].a.substr(1);
q["asd"].b; // Error
>q["asd"].b : any
>q["asd"] : { a: string; }
>q : s
>q : Pick<{ [key: string]: { a: string; }; }, string | number>
>"asd" : "asd"
>b : any

Expand All @@ -97,7 +97,7 @@ const d: { [key: string]: {a: string, b: string} } = q; // Error
>key : string
>a : string
>b : string
>q : s
>q : Pick<{ [key: string]: { a: string; }; }, string | number>

// Repro from #32484

Expand Down
Loading