템플릿 리터럴 타입으로 prop 이름을 만든다 — 매핑 타입이 union 멤버를 하나씩 K 로 돌고, as 가 `render${K}` 로 이름을 바꾼다.1

import * as React from 'react'

type RenderPropNames = 'Title' | 'Content' | 'Actions'

type RenderProps = {
  [K in RenderPropNames as `render${K}`]: () => React.ReactNode
}

type Props = RenderProps

/**
 * @example
 *
 * ```tsx
 * <DialogComponent
 *   renderTitle={() => <h2>Title</h2>}
 *   renderContent={() => <p>Content</p>}
 *   renderActions={() => (
 *     <div>
 *       <button onClick={handleClose}>Close</button>
 *       <button onClick={handleSubmit}>Submit</button>
 *     </div>
 *   )}
 * />
 * ```
 */
function Dialog({
  renderTitle,
  renderContent,
  renderActions,
}: Props) {
  return (
    <div data-scope="root">
      <div data-part="content">
        {renderTitle()}
        {renderContent()}
      </div>
      <div data-part="actions">{renderActions()}</div>
    </div>
  )
}

Footnotes

  1. TypeScript Handbook — Mapped Types, Key Remapping via as (TS 4.1+): “You can leverage features like template literal types to create new property names from prior ones”. K 가 멤버 하나일 때 템플릿 리터럴은 “produces a new string literal type by concatenating the contents”. ↩

`#350`