Skip to content

Item 56: Pay Attention to How Types Display

要点

  • 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.
  • 展示同一类型的方式有很多种,某些方式比其他方式更清晰。
  • TypeScript 提供了一些控制类型显示的工具,特别是 Resolve 泛型。要谨慎使用它来澄清类型显示并隐藏实现细节。
  • 考虑处理泛型类型的重要特殊情况,以改善类型显示。
  • 为你的泛型类型及其显示编写测试,以避免回归错误。

正文

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

💻 playground


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

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

💻 playground


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

💻 playground


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

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

💻 playground


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

💻 playground


ts
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


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

💻 playground


ts
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


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

💻 playground


ts
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


ts
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


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

💻 playground


ts
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

Released under the MIT License.