javascript를 메인으로 사용하는 프로젝트에
사정상 코드베이스를 타입스크립트로 마이그레이션 할 수 없을 때,
d.ts를 통해 타입스크립트의 힘을 활용하는 방법을 알아봅니다.
해당 내용은 사내에서 프로젝트를 진행하며 배운 내용을 정리한 것임을 말씀드립니다.
d.ts파일이란?
기본적으로 c언어의 header와 같은 역할을 합니다.
즉 각 모듈(파일)에 대한 인터페이스 역할만 하지, 모듈 내부의 타입 안정성에 대해서는 전혀 보장해주지 않습니다.
하지만 d.ts파일을 js파일의 타입 체킹에 이용할 수 있습니다.
[Front, 2022-09-14] Typescript d.ts 파일을 js 프로젝트에서 사용할 수 있을까?
d.ts 파일의 타입 선언 활용하기
d.ts 파일 내부에서는 타입 선언과 변수, 함수 선언을 사용할 수 있습니다.
구현은 내버려 두고, 해당 타입의 변수가 존재한다고 가정합니다.
중요한 것은 타입을 export 하는 것이 아니라 변수(선언)을 export 해야 다른 파일에서 해당 모듈을 활용할 수 있습니다.
export type MyModule ={}; // x
export declare const MyModule: MyType ; // O
네임스페이스와 같은 구 방식도 존재하지만, declare const와 내부 필드 선언으로 대체 가능합니다.
(그리고 더 직관적입니다.)
// MyModule.js
/**@type{import("./Counter").default} */
const Counter = { Container, ControlContainer, ControlButton };
// MyModule.d.ts
declare const Counter = {Container,ControlContainer,ControlButton};
d.ts 파일과 리액트 프로젝트
리액트 컴포넌트의 경우 함수 필드에 하위 컴포넌트를 추가하는 방식으로 컴파운드 컴포넌트 패턴을 자주 구현합니다.
이 경우 익명 함수로 컴포넌트를 구현할 경우 타입 오류가 발생합니다.
default에 해당하는 함수는 function으로 변경해 줍니다.
// MyModule.js
/**@type{import("./Counter").default} */
function MyFunction(){//...//}
MyFunction.SubConmponent = ()=><></>
// MyModule.d.ts
declare function MyFunction():JSX.Element;
declare function SubCmponent():JSX.Element;
MyFunction['SubConmponent']= SubCmponent;
d.ts 파일과 propTypes
프로젝트 전체를 타입스크립트로 마이그레이션 하는 것이 아니면,
특히 stroybook을 사용하는 경우, propTypes를 사용하는 것이 좋습니다.
이 경우 propTypes와 함수의 시그니처를 동기화 할 수 있습니다.
// MyModule.d.ts
declare function MyComponenent(props:MyProp):JSX.Element;
declare const MyComponenentPropTypes : React.WeakValidationMap<
Partial<Parameters<typeof MyComponent>[0]>
>;
// or ...
declare const ControlButtonPropTypes : React.WeakValidationMap<
MyProp
>;
MyComponenent['propTypes'] = ControlButtonPropTypes;
코드 내에서 타입 단언(assertion)
js 코드 내에서 디폴트 값을 사용할 경우, 타입스크립트 컴파일러는 리터럴 유니온 타입을 string으로 추론합니다.
이 경우 타입 단언이 필요한데, 아래와 같이 인라인 타입 주석을 추가하고, 오른쪽을 ()로 감싸주면 됩니다.
// MyModule.ts
// before
function ControlButton({
variant = 'outlined' // inferred as string ,
plus = false,
rounded = true,
onClick,
longPressBind = () => ({}),
})
// after
function ControlButton({
variant = /** @type{'outlined'} */ ('outlined'),
plus = false,
rounded = true,
onClick,
longPressBind = () => ({}),
})
위와 같이 작업하면서 딱히 레퍼헌스할 만한 자료들이 많이 없어서 난감했습니다.
혹시 더 자세한 내용이 궁금하시면 댓글로 질문해주세용
더 알아보기 :
https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
'FrontEnd' 카테고리의 다른 글
리액트 : 리렌더링 하도록 코딩하기 (부제 : memo 대신 useMemo) (0) | 2022.09.27 |
---|---|
리액트 쿼리 : 리액트 라우터와 연계하기 (6) | 2022.09.27 |
스토리북 개발팀이 알려주는 컨테이너 / 프리젠터 패턴 - Context API를 이용해 의존성 주입하기 (0) | 2022.09.22 |
소프트웨어 합성 : 트랜스듀서(Transducers) (0) | 2022.09.20 |
소프트웨어 합성 : 리듀서(reducer) (0) | 2022.09.20 |