-
Notifications
You must be signed in to change notification settings - Fork 13k
Description
Bug Report
This is a cut-down repro for an issue I encountered while fixing a bug in rtti. What I saw was the same types behaving differently when consumed inside this package (ie referencing the types directly in src/
) vs in downstream deps (ie referencing the emitted .d.ts
types in dist/
).
🔎 Search Terms
mapped type over union
mapped type over generic
🕗 Version & Regression Information
v4.4.4
v4.5 beta
v4.6 nightly (2 Nov 2021)
This is the behavior in every version I tried, and I reviewed the FAQ for entries about mapped types / homomorphic mapped types
⏯ Playground Link
Can't demonstrate in a single playground file.
💻 Code
/// file: test.ts
import {mappedUnionWithExportedType, mappedUnionWithPrivateType} from './api';
const a = mappedUnionWithExportedType({foo: 42}, {bar: 'bar'}); // type is {foo: number} | {bar: number}
'foo' in a ? a.foo.toFixed : a.bar.padStart; // OK
const b = mappedUnionWithPrivateType({foo: 42}, {bar: 'bar'}); // type is {}
'foo' in b ? b.foo.toFixed : b.bar.padStart;
// ^^^ ^^^ // ERRORS: 'foo' and 'bar' don't exist on {}
/// file: api.ts
import {useExportedType, usePrivateType} from './internal';
export const mappedUnionWithExportedType = <T extends unknown[]>(...args: T) => useExportedType(...args);
export const mappedUnionWithPrivateType = <T extends unknown[]>(...args: T) => usePrivateType(...args);
/// file: internal.ts
export declare function useExportedType<T extends unknown[]>(...args: T): ExportedMapped<T[any]>;
export declare function usePrivateType<T extends unknown[]>(...args: T): PrivateMapped<T[any]>;
export type ExportedMapped<Obj> = {[K in keyof Obj]: Obj[K]};
type PrivateMapped<Obj> = {[K in keyof Obj]: Obj[K]};
🙁 Actual behavior
As shown in test.ts
above, declaration emit type semantics change depending on whether an internal type is marked with export
or not. In the above example, the declarations emitted for api.d.ts
are:
export declare const mappedUnionWithExportedType: <T extends unknown[]>(...args: T) => import("./internal").ExportedMapped<T[any]>;
export declare const mappedUnionWithPrivateType: <T extends unknown[]>(...args: T) => { [K in keyof T[any]]: T[any][K]; };
🙂 Expected behavior
Mapping over a union type seems silly to begin with (indeed that was the rtti bug I was fixing), but regardless of that, I would expect declaration emit semantics to be preserved whether or not tsc
preserves an internal type in the emit, or expands it directly into a type expression.