Skip to content

Emitted mapped type in declarations loses modifiers Β #55002

@Andarist

Description

@Andarist

Bug Report

πŸ”Ž Search Terms

modifiers declaration dts emit mapped type omit pick

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried (at least since 4.7)

⏯ Playground Link

The repro case involves multiple files, 2 packages and declaration emit so I had to prepare a repository with it:
https://github.com/Andarist/mapped-type-emit-loses-modifiers-bug

πŸ’» Code

// packages/pkg-a/src/other.ts

// how Omit from lib is defined
type OmitReal<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
// what we see when we hover it
type OmitUnveiled<T, K extends string | number | symbol> = {
  [P in Exclude<keyof T, K>]: T[P];
};

export function test1<T, K extends string>(obj: T, k: K): OmitReal<T, K> {
  return {} as any;
}

export function test2<T, K extends string>(obj: T, k: K): OmitUnveiled<T, K> {
  return {} as any;
}

// packages/pkg-a/src/index.ts

import { test1, test2 } from "./other";

export function wrappedTest1<T, K extends string>(obj: T, k: K) {
  return test1(obj, k);
}

export function wrappedTest2<T, K extends string>(obj: T, k: K) {
  return test2(obj, k);
}

export type Obj = {
  a: number;
  readonly foo: string;
};

export const processedInternally1 = wrappedTest1({} as Obj, "a");
export const processedInternally2 = wrappedTest2({} as Obj, "a");

// packages/pkg-b/src/index.ts

import { wrappedTest1, wrappedTest2, Obj } from "pkg-a";

export const processedExternally1 = wrappedTest1({} as Obj, "a");
export const processedExternally2 = wrappedTest2({} as Obj, "a");

πŸ™ Actual behavior

The mapped type starts to lose modifiers when it's emitted even though modifiers are retained when the same thing is used within the package.

This can be seen in the emitted .d.ts for each of those packages. Both are using the same function and the same arguments but yet the type emitted in both places is different:

In pkg-a we can see this:

export declare const processedInternally1: {
    readonly foo: string;
};
export declare const processedInternally2: {
    foo: string;
};

In pkb-b we can see this:

export declare const processedExternally1: {
    foo: string;
};
export declare const processedExternally2: {
    foo: string;
};

It all starts with the fact that OmitReal and OmitUnveiled are not exported from packages/pkg-a/src/other.ts so packages/pkg-a/types/index.d.ts has to inline them in its declarations and both end up being emitted the same (here):

export declare function wrappedTest1<T, K extends string>(obj: T, k: K): { [P in Exclude<keyof T, K>]: T[P]; };
export declare function wrappedTest2<T, K extends string>(obj: T, k: K): { [P in Exclude<keyof T, K>]: T[P]; };

Their original declarations have different traits when it comes to preserving property modifiers though and thus this is a bug.

πŸ™‚ Expected behavior

I would expect the emitted declaration to behave in the same way as the original one.

Metadata

Metadata

Assignees

Labels

Fix AvailableA PR has been opened for this issueNeeds InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions