타입챌린지 26401 - JSON Schema to TypeScript

소파의 벨로그·2025년 5월 27일

타입챌린지

목록 보기
96/131

문제 링크

문제

주어진 Json 스키마에 상응하는 타입스크립트 타입을 반환하는 JSONSchema2TS를 구현하라

Implement the generic type JSONSchema2TS which will return the TypeScript type corresponding to the given JSON schema.

내 풀이

type PrimitiveTypeString='string'|'number'|'boolean'|'null'|'undefined'


interface PrimitiveTypeStringMapper{
  'string':string,
  'number':number,
  'boolean':boolean,
  'null':null,
  'undefined':undefined
}


interface JsonPrimitiveSchema<T extends PrimitiveTypeString=PrimitiveTypeString>{
  type:T
}
interface JsonEnumSchema<T extends PrimitiveTypeString=PrimitiveTypeString> extends JsonPrimitiveSchema<T>{
  enum:PrimitiveTypeStringMapper[T][]
}


interface JsonObjectSchemaBase{
  type:'object'
}

interface JsonObjectPropertySchema extends JsonObjectSchemaBase{
  properties:any
  required?: PropertyKey[]
}

type JsonObjectSchema=JsonObjectSchemaBase|JsonObjectPropertySchema

interface JsonArraySchema{
  type:'array'
  items?:JsonSchema
}

type JsonSchema=JsonEnumSchema|JsonPrimitiveSchema|JsonObjectSchema|JsonArraySchema


type JsonObjectPropertyType<S extends JsonObjectPropertySchema>=S['properties'] extends Record<string,JsonSchema>?
    S['required'] extends PropertyKey[]?
      {[R in keyof S['properties'] as R extends S['required'][number]?never:R]?:JSONSchema2TS<S['properties'][R]>}&{[R in keyof S['properties'] as R extends S['required'][number]?R:never]:JSONSchema2TS<S['properties'][R]>}
      :{[R in keyof S['properties']]?:JSONSchema2TS<S['properties'][R]>}
    :never

type MappedType<T>={[R in keyof T]:T[R]}

type JsonObjectType<S extends JsonObjectSchema>=
    S extends JsonObjectPropertySchema?
      MappedType<JsonObjectPropertyType<S>>
    :Record<string, unknown>

type JsonArrayType<S extends JsonArraySchema>=
  S['items'] extends JsonSchema?
    JSONSchema2TS<S['items']>[]
  :unknown[]


type JSONSchema2TS<T extends JsonSchema> =
  T extends JsonEnumSchema?T['enum'][number]:
  T extends JsonPrimitiveSchema?PrimitiveTypeStringMapper[T['type']]:
  T extends JsonArraySchema?JsonArrayType<T>:
  T extends JsonObjectSchema?JsonObjectType<T>:
  never

primitive타입과 enum 타입, 객체타입, 배열타입으로 나누어 구현하였다.

Enum 타입은 한 종류의 원시형 타입만 들어올 수 있어야 한다고 생각했다.

그래서 type과 enum의 타입이 상응해야 한다 생각해 다음과 같은 구조로 작성하였다.
우선 primativeSchema의 type에 들어가는 문자열('string','number')등을 제네릭으로 지정하였다.

이후 각 문자열에 해당하는 실제 타입에 맞는 mapper를 만들어 enum에 배열타입으로 넣어주었다.

이렇게 하면 일일히 number형 enum 스키마, string형 enum 스키마, 일반 스키마 등을 일일히 구현하지 않아도 된다.

다음은 배열을 확인한다. 스키마의 item이 없을 때의 기본값이 unknown[]을 구현했다.

객체인 경우 두가지 경우로 나뉘었다
required가 없는 경우 mappedType을 이용해 모든 값을 optional로 주었다.
required가 있는 경우 Required와 있는 경우와 없는 경우로 나누어 각각 optional과 required로 나누었다.

이때, eqaul의 부조리함으로 인해 MappedType이라는 감싸주는 타입을 만들었다.

0개의 댓글