TypeScript: 유형을 좁히는 이야기
어떻게 하시겠습니까???
첫 번째 시도는 API의 데이터를 공통 형식으로 다시 매핑하는 것입니다. 그런 다음 그 데이터를 통합하는 것이 정말 어렵다는 것을 깨달았습니다. 그래서 나는이 코드를 생각해 냈습니다.
type SomeKindOfInterfaceHere = { hello: string };
type AnotherInterface = { world: boolean };
interface MappedDataFromApi {
id: string | number;
data: string[] | SomeKindOfInterfaceHere | AnotherInterface;
}
function AReactComponent(props: MappedDataFromApi) {
if (props.data.hello) {
return <>display {props.data.hello} </>
}
if (props.data.world) {
return <>display {props.data.world} </>
}
return props.data.map(d => (<>display array item: {d}</>));
}
그것은 완벽하게 작동합니다. 잘 렌더링됩니다. 그러나 Typescript는 고함을 지르기 시작하고 내가 코드를 컴파일하는 것을 방해합니다.
Property ‘hello’ does not exist on type >‘SomeKindOfInterfaceHere | AnotherInterface | string[]’.
Property ‘hello’ does not exist on type ‘AnotherInterface’.(2339)
Typescript를 만족시키기 위해 내 코드를 다음으로 리팩토링합니다.
interface MappedDataFromApi {
id: string | number;
contentVR?: SomeKindOfInterfaceHere;
documentsInfo?: string[];
bundleInfo?: AnotherInterface;
}
function AReactComponent(props: MappedDataFromApi) {
if (props.contentVR) {
return <>display {props.contentVR.hello} </>
}
if (props.bundleInfo) {
return <>display {props.bundleInfo.world} </>
}
return props.documentsInfo && props.documentsInfo.map(d => (<>display array item: {d}</>));
}
물론 Typescript는 이제 더 나은 느낌을 받을 수 있습니다. 우리는 또 다른 문제를 만들었습니다. 일부는 다음과 같이 말할 수 있습니다.
Hey mate, this interface is bad. It has so many
?
It is arbitrary. How can I re-use it elsewhere? How can I maintain your code once you not here? Those properties look mysterious.
잔인하지만 합리적인 소리!
알겠습니다. 다시 한 번 시도해 보겠습니다. 내 인터페이스를 더 작은 조각으로 나눕니다. 깔끔해 보이긴 하지만
?
...interface VerificationRequest {
uuid: string;
content: SomeKindOfInterfaceHere;
}
interface SingleVerification {
id: number;
documents: string[];
}
interface Bundle {
id: number;
info: AnotherInterface;
}
type MappedDataFromApi = VerificationRequest | SingleVerification | Bundle;
function AReactComponent(props: MappedDataFromApi) {
if (props.content) {
return <>display {props.content.hello} </>
}
if (props.info) {
return <>display {props.info.world} </>
}
return props.documents.map(d => (<>display array item: {d}</>));
}
Brrrrr, Typescript가 이전과 동일한 문제로 다시 나에게 고함을 지릅니다.
Property ‘content’ does not exist on type ‘MappedDataFromApi’.
Property ‘content’ does not exist on type ‘SingleVerification’.(2339)
운 좋게도 Typescript에는 더 나은 코드를 작성하고 이 경우 좋은 타이핑을 할 수 있는 이러한 보석이 있습니다.
유형 술어 사용
이 방법을 사용하면 Typescript가 내가 작업 중인 인터페이스의 종류를 감지하도록 지원하는 몇 가지 유틸리티 기능을 추가할 수 있습니다. 코드는 다음과 같습니다.
function isVerificationRequest(props: MappedDataFromApi): props is VerificationRequest {
return !!(props as VerificationRequest).content;
}
function isSingleVerification(props: MappedDataFromApi): props is SingleVerification {
return Array.isArray((props as SingleVerification).documents);
}
function isBundle(props: MappedDataFromApi): props is Bundle {
return !!(props as Bundle).info;
}
function AReactComponent(props: MappedDataFromApi) {
if (isVerificationRequest(props)) {
return <>display {props.content.hello} </>
}
if (isBundle(props)) {
return <>display {props.info.world} </>
}
return props.documents.map(d => (<>display array item: {d}</>));
}
아름답죠? 👏👏👏
한 가지는 이 스타일이 최종 JS 코드 크기를 조금 더 크게 만든다는 것입니다. Typescript Playground에서 JS 컴파일 버전을 확인할 수 있습니다.
차별적 노동조합
이 방법을 사용하면 리터럴 유형의 공통 속성을 인터페이스에 추가할 수 있습니다. 코드는 다음과 같습니다.
interface VerificationRequest {
uuid: string;
content: SomeKindOfInterfaceHere;
kind: 'verification-request';
}
interface SingleVerification {
id: number;
documents: string[];
kind: 'single-verification';
}
interface Bundle {
id: number;
info: AnotherInterface;
kind: 'bundle';
}
type MappedDataFromApi = VerificationRequest | SingleVerification | Bundle;
function AReactComponent(props: MappedDataFromApi) {
switch (props.kind) {
case 'verification-request':
return <>display {props.content.hello} </>
case 'single-verification':
return props.documents.map(d => (<>display array item: {d}</>));
case 'bundle':
return <>display {props.info.world} </>
default:
return null;
}
}
보기에도 깔끔합니다. 이 스타일로 Exhaustiveness checking을 만들 수도 있습니다. 그러나 다른 한편으로 인터페이스를 다른 곳에서 재사용하려면 공통 속성을 생략하거나 데이터 컬렉션에 수동으로 추가해야 합니다. 그렇지 않으면 Typescript가 다시 한 번 소리를 지릅니다.
내가 말하는 내용은 다음과 같습니다.
// drop "kind" by create a new Omit type
type NewSingleVerification = Omit<SingleVerification, "kind">
function getSingleVerification(): NewSingleVerification {
return {
id: 1,
documents: ['education', 'license'],
};
}
// OR
function getSingleVerification(): SingleVerification {
return {
id: 1,
documents: ['education', 'license'],
// manual add this
kind: 'single-verification',
};
}
이것은 UI 문제가 비즈니스 로직에 관련되어서는 안 되는 부분을 포함하기 때문에 제게는 큰 단점입니다.
결론
이것들은 내가 생각해낼 수 있는 모든 해결책입니다. 각각의 단점이 있지만 적어도 마지막 2개는 유형 검사에 대한 거의 우리 팀의 우려를 다룰 수 있고 모든 사람이 코드를 쉽게 이해할 수 있습니다.
다른 해결 방법이 있으면 아래에 의견을 보내주십시오.
읽어주셔서 감사합니다
Reference
이 문제에 관하여(TypeScript: 유형을 좁히는 이야기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/quanpham/typescript-a-narrowing-type-story-19g3텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)