Skip to content

Latest commit

 

History

History
171 lines (126 loc) · 7.91 KB

type-display.md

File metadata and controls

171 lines (126 loc) · 7.91 KB

Item 56: Pay Attention to How Types Display

Things to Remember

  • There are many valid ways to display the same type. Some are clearer than others.
  • TypeScript gives you some tools to control how types display, notably the Resolve generic. Make judicious use of this to clarify type display and hide implementation details.
  • Consider handling important special cases of generic types to improve type display.
  • Write tests for your generic types and their display to avoid regressions.

Code Samples

type T123 = '1' | '2' | '3';
//   ^? type T123 = "1" | "2" | "3"

💻 playground


type T21 = '2' | '1';
//   ^? type T21 = "2" | "1"

type T123 = '1' | '2' | '3';
//   ^? type T123 = "2" | "1" | "3"

💻 playground


type PartiallyPartial<T, K extends keyof T> =
  Partial<Pick<T, K>> & Omit<T, K>;

💻 playground


interface BlogComment {
  commentId: number;
  title: string;
  content: string;
}

type PartComment = PartiallyPartial<BlogComment, 'title'>;
//   ^? type PartComment =
//          Partial<Pick<BlogComment, "title">> &
//          Omit<BlogComment, "title">

💻 playground


type Resolve<T> = T extends Function ? T : {[K in keyof T]: T[K]};

💻 playground


type PartiallyPartial<T, K extends keyof T> =
  Resolve<Partial<Pick<T, K>> & Omit<T, K>>;

type PartComment = PartiallyPartial<BlogComment, 'title'>;
//   ^? type PartComment = {
//          title?: string | undefined;
//          commentId: number;
//          content: string;
//      }

💻 playground


type ObjIdentity<T> = {[K in keyof T]: T[K]};

💻 playground


type S = ObjIdentity<string>;
//   ^? type S = string
type N = ObjIdentity<number>;
//   ^? type N = number
type U = ObjIdentity<'A' | 'B' | 'C'>;
//   ^? type U = "A" | "B" | "C"

💻 playground


type F = ObjIdentity<(a: number) => boolean>;
//   ^? type F = {}

💻 playground


type D = Resolve<Date>;
//   ^? type D = {
//        toLocaleString: {
//            (locales?: Intl.LocalesArgument,
//             options?: Intl.DateTimeFormatOptions | undefined): string;
//            (): string;
//            (locales?: string | string[] | undefined,
//             options?: Intl.DateTimeFormatOptions | undefined): string;
//        };
//        ... 42 more ...;
//        [Symbol.toPrimitive]: {
//            ...;
//        };
//      }

💻 playground


interface Color { r: number; g: number; b: number; a: number };
type Chan = keyof Color;
//   ^? type Chan = keyof Color
type ChanInline = Resolve<keyof Color>;
//   ^? type ChanInline = "r" | "g" | "b" | "a"

💻 playground


type FullComment = PartiallyPartial<BlogComment, never>;
//   ^? type FullComment = {
//             title: string;
//             commentId: number;
//             content: string;
//           }

💻 playground


type PartiallyPartial<T extends object, K extends keyof T> =
  [K] extends [never]
  ? T  // special case
  : T extends unknown  // extra conditional to preserve distribution over unions
  ? Resolve<Partial<Pick<T, K>> & Omit<T, K>>
  : never;

type FullComment = PartiallyPartial<BlogComment, never>;
//   ^? type FullComment = BlogComment

💻 playground