programing

어레이 속성을 중단하지 않고 TypeScript 딥 부분 매핑 유형을 구현하는 방법

prostudy 2022. 3. 14. 21:37
반응형

어레이 속성을 중단하지 않고 TypeScript 딥 부분 매핑 유형을 구현하는 방법

TypeScript의 부분 매핑 유형을 인터페이스에 재귀적으로 적용하는 동시에 어레이 리턴 유형이 포함된 키를 중단하지 않는 방법에 대한 아이디어는?

다음 접근방식은 충분하지 않았다.

interface User {  
  emailAddress: string;  
  verification: {
    verified: boolean;
    verificationCode: string;
  }
  activeApps: string[];
}

type PartialUser = Partial<User>; // does not affect properties of verification  

type PartialUser2 = DeepPartial<User>; // breaks activeApps' array return type;

export type DeepPartial<T> = {
  [ P in keyof T ]?: DeepPartial<T[ P ]>;
}

좋은 생각 있어?

업데이트: 수락된 답변 - 현재로선 더 좋고 일반적인 해결책.

다음과 같이 유형과 맵핑된 유형 두 개가 교차하는 임시 해결 방법을 발견하였다.가장 눈에 띄는 단점은 배열 반품 유형이 있는 파손된 키를 복원하려면 속성 재정의를 제공해야 한다는 점이다.

예시

type PartialDeep<T> = {
  [ P in keyof T ]?: PartialDeep<T[ P ]>;
}
type PartialRestoreArrays<K> = {
  [ P in keyof K ]?: K[ P ];
}

export type DeepPartial<T, K> = PartialDeep<T> & PartialRestoreArrays<K>;

interface User {  
 emailAddress: string;  
 verification: {
   verified: boolean;
   verificationCode: string;
 }
 activeApps: string[];
}

export type AddDetailsPartialed = DeepPartial<User, {
 activeApps?: string[];
}>

그렇게

TS 2.8 및 조건부 유형을 사용하면 다음과 같이 간단히 작성할 수 있다.

type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends Array<infer U>
    ? Array<DeepPartial<U>>
    : T[P] extends ReadonlyArray<infer U>
      ? ReadonlyArray<DeepPartial<U>>
      : DeepPartial<T[P]>
};

또는 을 가지고[]대신에Array<>즉, 다음과 같다.

type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends (infer U)[]
    ? DeepPartial<U>[]
    : T[P] extends Readonly<infer U>[]
      ? Readonly<DeepPartial<U>>[]
      : DeepPartial<T[P]>
};

당신은 이것과 다른 유용한 종류들에 대해 https://github.com/krzkaczor/ts-essentials 패키지를 체크아웃하는 것이 좋을 것이다.

업데이트 2018-06-22:

이 대답은 놀라운 조건부 유형 기능이 TypeScript 2.8에서 발표되기 1년 전에 작성되었다.그래서 이 대답은 더 이상 필요하지 않다.TypeScript 2.8 이상에서 이 동작을 얻는 방법은 아래 @krzystof-kaczor의 새로운 답변을 참조하십시오.


좋아, 여기 내가 미친것 같지만 완전히 일반적인 해결책(TypeScript 2.4 이상 필요)에 대한 최선의 시도가 있어. 그럴 가치가 없을지도 모르지만, 그것을 사용하고 싶다면, 내 손님이 되어라.

먼저, 우리는 몇 가지 유형 수준의 부울 논리가 필요하다.

type False = '0'
type True = '1'
type Bool = False | True
type IfElse<Cond extends Bool, Then, Else> = {'0': Else; '1': Then;}[Cond];

여기서 네가 알아야 할 건 그 타입이IfElse<True,A,B>로 평가하다.A그리고IfElse<False,A,B>로 평가하다.B.

이제 우리는 레코드 유형을 정의한다.Rec<K,V,X>, 키가 있는 물체K및 값 유형V어디에Rec<K,V,True>자산이 요구됨을 의미하며,Rec<K,V,False>속성이 선택적임을 의미한다.

type Rec<K extends string, V, Required extends Bool> = IfElse<Required, Record<K, V>, Partial<Record<K, V>>>

이쯤 되면 자네에게 갈 수 있다.User그리고DeepPartialUser유형. 장군을 묘사해 보자.UserSchema<R>우리가 신경쓰는 모든 재산은 필수적이거나 선택적이거나 둘 중 하나일 경우, 그 여부에 따라 달라진다.R이다True또는False:

type UserSchema<R extends Bool> =
  Rec<'emailAddress', string, R> &
  Rec<'verification', (
    Rec<'verified', boolean, R> &
    Rec<'verificationCode', string, R>
  ), R> &
  Rec<'activeApps', string[], R>

못생겼지?하지만 우리는 마침내 두 가지 모두를 설명할 수 있다.User그리고DeepPartialUser다음과 같이.

interface User extends UserSchema<True> { } // required
interface DeepPartialUser extends UserSchema<False> { }  // optional

그리고 그것을 행동으로 보여라:

var user: User = {
  emailAddress: 'foo@example.com',
  verification: {
    verified: true,
    verificationCode: 'shazam'
  },
  activeApps: ['netflix','facebook','angrybirds']
} // any missing properties or extra will cause an error

var deepPartialUser: DeepPartialUser = {
  emailAddress: 'bar@example.com',
  verification: {
    verified: false
  }
} // missing properties are fine, extra will still error

여기 있다.그게 도움이 되길 바래!

나는 @krzystof의 대답으로 시작했지만, 그 이후 에지 케이스에 부딪히면 계속 반복해 왔다.특히 아래 에지 케이스는 주어진 기본 객체 값(즉,)에 기초한다.T[P]):

  • any
  • any[]
  • ReadonlyArray<any>
  • Map
  • Set
type NonAny = number | boolean | string | symbol | null;
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends NonAny[] // checks for nested any[]
    ? T[P]
    : T[P] extends ReadonlyArray<NonAny> // checks for nested ReadonlyArray<any>
    ? T[P]
    : T[P] extends (infer U)[]
    ? DeepPartial<U>[]
    : T[P] extends ReadonlyArray<infer U>
    ? ReadonlyArray<DeepPartial<U>>
    : T[P] extends Set<infer V> // checks for Sets
    ? Set<DeepPartial<V>>
    : T[P] extends Map<infer K, infer V> // checks for Maps
    ? Map<K, DeepPartial<V>>
    : T[P] extends NonAny // checks for primative values
    ? T[P]
    : DeepPartial<T[P]>; // recurse for all non-array and non-primative values
};

NonAny유형을 사용하여 확인any가치

ts-toolbelt를 사용할 수 있고, 어떤 깊이에서도 타입에 대한 조작을 할 수 있다.

귀하의 경우 다음과 같은 것이 될 수 있다.

import {O} from 'ts-toolbelt'

interface User {  
    emailAddress: string;  
    verification: {
      verified: boolean;
      verificationCode: string;
    }
    activeApps: string[];
}

type optional = O.Optional<User, keyof User, 'deep'>

그리고 (디스플레이를 위해) 심층적으로 계산하려면Compute그러기 위해서

참조URL: https://stackoverflow.com/questions/45372227/how-to-implement-typescript-deep-partial-mapped-type-not-breaking-array-properti

반응형