Typescript 부분적이지만 정의되지 않음

Partial는 가장 일반적인 유틸리티 유형 중 하나라고 생각합니다.

편리합니다. 속성을 건너뛸 수 있습니다.

예를 들어 데이터베이스의 특정 필드를 업데이트하려면

type abc= Partial<{a:number, b:string, c:boolean}>

const updateDB=(data:abc)=>{}

updateDB({a:1, c:true})


그러나 Partial는 모든 속성을 undefined와 통합합니다.

updateDB({a:undefined, b:undefined, c:undefined})


playground

모든 사람이 원하는 것은 아니기 때문에 문제가 될 수 있습니다undefined. 일부 데이터베이스는 허용할 수 없습니다undefined(예: Firestore).

이 문제를 어떻게 해결할 수 있습니까? 속성을 선택 사항으로 지정하는 동시에 데이터베이스 오염undefined을 방지하려면 어떻게 해야 합니까?

제네릭 및 매핑된 유형을 사용하여 이 문제를 해결할 수 있습니다.

type abc= {a:number, b:string, c:boolean}

type PartialButNoUndefined <Type extends Record<string,unknown>,Data extends Record<string,unknown>> = 
    keyof Data extends keyof Type
    ? {
        [K in keyof Data]:Data[K] extends Type[K]?Data[K]:`type of property ${K & string} is incorrect`
    }
    :`unknown property:${Exclude<keyof Data, keyof Type> & string}` 


type allProps = PartialButNoUndefined<abc,{a:1,b:"b",c:true}> // {a: 1, b: "b",c: boolean }
type partialProps = PartialButNoUndefined<abc,{b:"b"}> // { b:string }
type propWithIncorrectValue = PartialButNoUndefined<abc,{a:1, b:true}> // {a,:1, b:"type of property b is incorrect" }
type unknownProps = PartialButNoUndefined<abc,{a:1, z:5, u:true}> // "unknown property:z" | "unknown property:u"


playground
PartialButNoUndefined가 하는 일:
  • 알 수 없는 속성 감지
  • 잘못된 속성 유형 감지
  • 한도

  • 보시다시피 이제 모든 멤버는 undefined 와 결합할 필요 없이 선택 사항입니다.

    마지막 단계는 PartialButNoUndefinedupdateDB 함수에 구현하는 것입니다.

    const updateDB=<T extends Record<string,unknown>>(data:PartialButNoUndefined<abc,T>)=>{}
    


    좋아, 한번 해보자!

    updateDB({a: 1, b: "b",c: true }) // expect ok
    updateDB({b:"b"}) // expect ok
    updateDB({c:undefined}) // expect error
    


    playground



    흠, 뭔가 이상해...왜?

    이는 Typescript가 T 인수에서 data를 추론할 수 없기 때문입니다.

    일반적으로 인수에서 유형을 추론하는 방법을 기억하십시오.

    const updateDB=<T extends Record<string,unknown>>(data:T)=>{}
    


    데이터 인수 타입으로 naked type 매개변수를 할당하고 이것이 제네릭 추론이 작동하는 이유입니다(지금은 이것이 정확하지 않고 제네릭 추론은 non-naked type 매개변수에도 작동하지만 지금 그대로 이해하시면 됩니다. 자세히 설명하는 다른 게시물 열기)

    하지만 이제 우리는

    const updateDB=<T extends Record<string,unknown>>(data:PartialButNoUndefined<abc,T>)=>{}
    


    그러나 이제 데이터 인수 유형으로 기본이 아닌 유형 매개변수PartialButNoUndefined<abc,T>를 할당합니다.

    그래서 우리는 이것을 어떻게 해결할 것입니까?

    이제 일반적인 추론이 작동하기 위한 조건, 즉
    기본 유형 매개변수를 데이터 인수 유형으로 지정

    그리고 우리는 그렇게 할 것입니다

    const updateDB=<T extends Record<string,unknown>>(data:T extends never? T: PartialButNoUndefined<abc,T>)=>{}
    


    playground



    그리고 붐, 작동합니다!

    하지만 어떻게? 무슨 일이야?

    재검토 해보자:
  • 이제 일반 추론이 작동하려면 네이키드 매개변수 유형Tdata 인수에 할당해야 한다는 것이 분명합니다
  • .
  • 하지만 일반Tdata 유형으로 사용하고 싶지 않고 PartialButNoUndefined<abc,T> 우리의 data 유형
  • 을 원합니다.
  • 따라서 data: T extends never? T: PartialButNoUndefined<abc,T>는 Typescript에 다음과 같이 말합니다.

    이는 dataPartialButNoUndefined<abc,T> 확장되지 않고 조건이 T를 유형으로 반환하기 때문에 가능합니다.

    즉, 이제 never 에 대해 걱정할 필요 없이 선택적 유형을 만들 수 있습니다.

    마지막으로 첫 번째를 단순화하겠습니다. 이제 유형 검사에 의존할 수 있으므로 설명 유형을 삭제할 수 있습니다.

    type abc= {a:number, b:string, c:boolean}
    
    type PartialButNoUndefined <Type extends Record<string,unknown>,Data extends Record<string,unknown>> = 
        {
            [K in keyof Data]: Type[K extends keyof Type ? K:never]
        }
    
    const updateDB=<T extends Record<string,unknown>>(data:T extends never? T: PartialButNoUndefined<abc,T>)=>{}
    
    updateDB({a: 1, b: "b",c: true }) // ok
    updateDB({b:"b"}) // ok
    updateDB({a:true, b:"abc",c:undefined}) // expect error
    updateDB({b:"abc",z:1}) // expect error
    


    playground



    이것은 더 깨끗해 보이고 더 간결한 유형 검사가 있습니다.
    PartialButNoUndefined<abc,T>를 제거하면 흥미로운 점이 있습니다.

    type abc= {a:number, b:string, c:boolean}
    
    type PartialButNoUndefined <Type extends Record<string,unknown>, Data extends Record<string,unknown>> = 
        {
            [K in keyof Data]: Type[K extends keyof Type ? K:never]
        }
    
    const updateDB=<T extends Record<string,unknown>>(data: PartialButNoUndefined<abc,T>)=>{}
    
    updateDB({a: 1, b: "b",c: true }) // ok
    updateDB({b:"b"}) // ok
    updateDB({a:true, b:"abc",c:undefined}) // expect error
    updateDB({b:"abc",z:1}) // expect error
    




    playground

    여전히 작동합니다

    그러나 왜 더 일찍 작동하지 않고 undefined에 의존해야 합니까?

    불행히도 나는 모른다. 그러한 행동은 문서에 설명되어 있지 않지만 아마도 내가 모르는 어떤 패치 노트에 설명되어 있을 것이다.

    이유 아시는분은 댓글 달아주세요

    업데이트: 오늘 작업을 더 간단하게 수행할 수 있는 방법이 있습니다: exactOptionalPropertyTypes
  • 좋은 웹페이지 즐겨찾기