TypeScript Type Guard를 안전하고 최신 상태로 유지(간단한 솔루션)

12674 단어 typescripttypeguard
이 문서는 그의 TypeScript: Keeping Type Guards Safe and Up To Date article에서 공유한 솔루션 개선에 관한 것입니다.

사진 제공: Brian McGowan on Unsplash


유형 가드를 사용하면 런타임, 알 수 없는 개체(백엔드에서 오는 데이터를 생각)가 지정된 유형을 존중하는지 확인할 수 있습니다. 유형 가드는 unknown 매개변수를 받고, 모든 속성이 있는지 확인하고, 특정 유형을 존중하는 개발자가 작성한 함수에 지나지 않습니다. 따라서 TypeScript는 매개변수가 올바르게 입력되었다고 안전하게 가정할 수 있습니다.

예: 주어진 유형

interface Person {
  name: string
  age: number
}


이것은 위의 유형 가드입니다Person.

function isPerson(value: unknown): value is Person {
  return (
    typeof value === 'object' &&
    value &&
    value.hasOwnProperty('name') &&
    value.hasOwnProperty('age') &&
    typeof value.name === 'string' &&
    typeof value.age === 'number'
  )
}


위 유형 가드의 주요 문제점은 무엇입니까? 확장되지 않습니다. Person 유형을 업데이트하면 TypeScript가 오류를 발생시키지 않습니다. 유형을 다음으로 변경한다고 상상해보십시오.

interface Person {
  name: string
  age: number
  something: string // <-- a new property
}


TypeScript는 불평하지 않지만 isPerson 함수는 이제 구식입니다.

그의 TypeScript: Keeping Type Guards Safe and Up To Date 기사(제목을 훔쳤습니다. 죄송합니다, Michal)에서 이 문제를 이미 논의했으며 스케일링 작업 솔루션을 제안했습니다.

문제와 그 해결책을 완전히 이해하려면 그의 기사를 읽으십시오. 이 기사에서는 그의 접근 방식을 약간의 차이를 두고 사용하여 (제 생각에는) 더 간단한 솔루션에 도달했습니다. 차이점은 다음과 같습니다.
  • isPlainObject를 확장하는 대신 global.object 유형 가드 사용
  • 실제 유형 가드
  • 로 되돌리기isPerson

    1. global.object를 확장하는 대신 isPlainObject 유형 가드 사용



    가능한 한 TypeScript의 전역 확장을 피합니다. 다음 isPlainObject은 TypeScript가 hasOwn/hasOwnProperty를 통해 확인된 속성이 사실상 개체 자체의 일부이지만 global.object를 확장하지 않는다는 것을 인식할 수 있는 간단한 해결 방법입니다.

    interface PlainObject {
      hasOwnProperty<K extends string>(key: K): this is Record<K, unknown>
    
      // Object.hasOwn() is intended as a replacement for Object.hasOwnProperty(). See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn
      hasOwn<K extends string>(key: K): this is Record<K, unknown>
    }
    
    function isPlainObject(value: unknown): value is PlainObject {
      return !!value && typeof value === 'object' && !Array.isArray(value)
    }
    


    2. isPerson을 실제 유형 가드로 되돌리기



    Michal은 구문 분석기를 만든 다음 이를 사용하여 유형 가드를 만들 것을 제안했습니다. 내 제안은 파서의 특전(확장 가능한 유형 가드)과 표준 유형 가드(단순성)를 활용하는 것입니다.

    이것은 두 가지 접근 방식을 혼합한 결과입니다.

    function isPerson(value: unknown): value is Person {
      if (!isPlainObject(value)) return false
    
      if (!value.hasOwnProperty('name')) return false
      if (!value.hasOwnProperty('age')) return false
    
      const { name, age } = value
    
      if (typeof name !== 'string') return false
      if (typeof age !== 'number') return false
    
      // @ts-expect-error: turn off "obj is declared but never used."
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const obj: Person = { name, age }
    
      return true
    }
    


    대부분의 마법은 다음 줄에서 나옵니다.

    // @ts-expect-error: turn off "obj is declared but never used."
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const obj: Person = { name, age }
    


    매번 새 개체를 만들고 반환하는 Michal의 솔루션이 마음에 들지 않았기 때문에 이것은 필요합니다. 나는 기계의 메모리를 많이 사용하는 응용 프로그램 작업에 익숙하며 생성하는 개체가 적을수록 좋습니다. 객체를 생성하는 것과 객체를 반환하는 것의 차이점은 무엇입니까?
    (V8 팀에서 작업한 크롬 브라우저 내부의 Javascript VM) 비소비 변수(다른 함수에 전달되지 않고 반환되지 않는 변수)와 관련된 코드가 함수를 떠나지 않는다고 장담합니다. 다른 범위와 공유되지 않으며 런타임 관점에서 완전히 쓸모가 없기 때문에 컴파일 시간에 제거됩니다. 따라서 개체는 런타임에 생성되지 않으며 가비지 수집기 또는 보육원을 통과하지 않습니다.

    TypeScript playground에서 위의 예를 가지고 놀 수도 있습니다. 여기에서 선택적 속성을 추가하여 어떻게 관리할 수 있는지 보여줍니다.

    업데이트



    새로운 선택적 속성으로부터 보호하는 데 도움이 되는 작은 변화도 살펴보세요 😊

    결론



    his solution으로 눈을 뜨게 해준 , Michal의 구현을 발전시키는 데 도움을 준 , JS VM에 대한 내부 정보를 제공한 에게 감사합니다.

    좋은 웹페이지 즐겨찾기