모듈 가져와서 사용할 때 타입을 선언해주어야함.
예를들어 lodash
라는 라이브러리를 사용할때. lodash.d.ts
파일을 만들어주어야 한다.
//lodash.d.ts
declare module 'lodash' {
interface Lodash{
camelCase(str: string): string,
uniq<T>(arr: T[]): T[], // 타입추론
cloneDeep<T>(arg: T): T
}
const lodash: Lodash;
export default lodash;
};
//main. ts
import lodash from 'lodash';
const str = "aaasaddsd asdvc";
const newStr = lodash.camelCase(str);
const nums = [1,2,3,3,4,4];
const newNums = lodash.uniq(nums);
const obj = {
a: {x: 1},
b: [7, 8]
}
const newObj = lodash.cloneDeep(obj);
메소드를 쓰면 쓸수록 작성해야할 타입선언이 많아진다. 누군가가 만들어놓은 타입선언을 갖다써보자!
없으면...만들어야겠지...?😫
npm info @types/lodash
로 누군가가 만들어놓은 타입이 존재하는지 체크할 수 있다.
그다음 npm i @types/lodash -D
로 설치!
이후 tsconfig
에서 수정해줘야함
//tsconfig.json
"compilerOptions":{
....
"typeRoots":["node_modules/@types"]
}
요래 수정해주면 노드모듈에 깔린 타입선언들을 활용할 수 있음.
다만 import lodash from 'lodash';
이렇게 사용할수가 없다. export default
가아닌 export
기 때문
import * as lodash from 'lodash'
//or
//tsconfig.json
"compilerOptions":{
....
"esModuleInterop": true
}
//위와같이 작성하면 아래와 같이 편하게 사용 가능
import lodash from 'lodash'
참고로 npm에 들어가면 ts
로 작성되어있는 라이브러리를 확인해볼 수 있다!
안되어있다면...ㅎㅎ
js로 만든 파일을 ts로 가져오게 만들고싶다.
//tsconfig.json
"compilerOptions":{
....
"allowJs":true,
}
JS파일 쓸수있게 설정해두면, js로만든 파일을 가져올 수 있다.
이때 JS의 모든 타입은 any
가 된다.
=> TS를 쓰는 의미 상실. 해결해보자
//myUtils.js
export const add = (a,b) => a+b;
export const subtract = (a,b) => a-b;
export default { add, subtract };
//myUtils.d.ts
//이름이 js파일과 같아야함. 다르다면 tsconfig.json의 compilerOptions - paths에 경로별칭을 적어준 뒤, 별칭을 import해주면 된다.
interface DefaultExport{
add: (a:number, b:number): number,
subtract: (a:number, b:number): number
};
declare const de: DefualtExport;
export default de;
//tsconfig.json
...
"include":[
"src/**/*.ts",
"js/**/*.ts",
]
//main.ts
import { add, subtract } from "../js/myUtils.js";
const a = add(4,null); // 타입에러. js였다면 타입에러 없이 진행됨
const c = subtract(9,3); // 6
ts
파일 가져올땐 기본적으로 확장자를 쓰지 않는다.
//myUtils.ts
export interface Add{
add: (a:number, b:number): number,
};
//main.ts
import { Add } from './myUtils'
const newAdd: Add = (x,y) => x+y
default로 내보내고 싶다면..
//myUtils.ts
export namespace Utils{
//...add와 subtract에 대한 interface 내용
}
const add: Utils.Add = (a,b) => a+b;
const subtract: Utils.Subtract = (a,b) => a-b;
export default{
name: 'My utils',
add
subtract
};
//main.ts
import utils from './myUtils';
import type { Utils } from "./myUtils"
const add: Utils.Add = (x,y) => x+y;
자세한건 공식문서에 나와있다.
include
: 포함(우선순위 낮음)exclude
: 제외(우선순위 보통)files
: 포함(우선순위 높음)exclude
를 제외한 모든 파일이 ts > js 컴파일됨
extends
: 다른 tsconfig
파일을 확장하여 사용tsc
명령어에 적용된 config들을 보려면 --showConfig
키워드를 뒤에 추가해주면 된다.strict
: 엄격한 타입검사 활성화target
: ts > js변환할때 JS버전 명시lib
: 컴파일에서 사용할 라이브러리 지정(js내부 라이브러리)module
: 사용할 모듈 방식 지정moduleResolution
: 컴파일러가 사용할 모듈 해석 방식(node, bundler 등) es5이상에서만 bundler 추천paths
: 위에서 설명했던 경로 별칭jsx
: jsx의 출력방식 제어. 리액트와 같이 사용할때!outDir
: 컴파일된 js의 파일 저장위치 설정//Partial => 필수 속성을 선택 속성으로 바꿔줌
interface User{
name: string,
age: number
}
const userA: User = {
name:"A", // age가 없어서 에러
}
//제네릭으로 사용
const userB: Partial<User> = {
name:"A"
}
//Required => 선택 속성을 필수 속성으로 바꿔줌. Partial과 정확히 반대여서 생략
//ReadOnly => 읽기전용. 할당,삭제불가 만들어줌
//Record<Key, Type>
type Nmaes = "neo" | "lewis"
type RecordNames = {
neo: number,
lewis: number
}
const developers: Record<Names, number> = {
neo: 12,
lewis: 13
}
//Pick<타입, 가져올 타입들> => 주어진 객체타입에서 원하는 속성만 가져와서 새로운 객체타입 반환
interface User{
name: string,
age: number,
email:string,
isValid: boolean
}
interface PickUser{
name: string,
email: string,
}
const user: Pick<User, 'name' | 'email'> = {
name: "Neo",
email: "aaa@aaa.aaa"
}
//Omit => Pick과 정확히 반대여서 생략
//Exclude<Types, Type> => Types에서 특정 Type을 제외한 타입만 허용
type T = string | number | boolean;
const a: Exclude<T, number> = 'Only string';
const b: Exclude<T, number> = 1234;
//Extract => Exclude와 정확히 반대여서 생략
//ReturnType<type> => 반환한 type으로 구성된 타입을 할당
function hello(msg: string){
return `Hello ${msg}`;
}
const a: ReturnType<typeof hello> = "hi";
//Awaited => 반환된 데이터의 데이터타입으로 할당
const img = getImg(src);
const el = Awaited<typeof img> | null = document.querySelector('photo');
vite는 webpack을 대신하는 최신 번들러!
CRA대신 사용할수도 있다.
강의에 다루지 않았던 tsconfig.json
의 옵션들을 더 알아보겠습니다!
removeComments
: 주석을 제거할 건지 말지 결정noEmit
: 컴파일러 출력으로 JS등 파일,폴더 생성 방지(Babel같은 도구 사용하는 경우 필요할 수 있음)allowImportingTsExtensions
: ts
확장자 명시 허용. isolatedModules
: 각 파일을 독립된 모듈로 처리하도록 강제resolveJsonModule
: JSON파일을 ES6모듈로서 가져와 사용할 수 있음(가져와서 타입추론 할수있게).moduleResolution
의 값이 node16, nodenext, bundler
인경우에만 사용 가능하다!useDefineForClassFields
: Obect.defineProperty
를 사용해 클래스필드를 정의하게 만듬.this.a = a
로 할당함noUnusedLocals
: 사용하지 않는 지역변수에 대해 컴파일러가 오류를 발생시킴.noUnusedParametes
: 사용하지 않는 매개변수에 대해 컴파일러가 오류를 발생시킴.noFallthroughCasesInSwitch
: switch문 내부 break없을때 오류발생skipLibCheck
: 선언 파일의 타입검사를 건너뜀!baseUrl
: 모듈 해석에 사용할 기준경로를 지정. 보통 paths
옵션과 같이 사용함allowJs
: .js, .jsx파일 입력 컴파일러 허용.(ts 마이그레이션 할때 js랑ts가 공존하기위해 보통 사용)checkJs
: .js, .jsx 파일의 타입 검사를 수행함. (JSDoc 주석을 사용해 타입 선언할 수 있음. 이 역시 마이그레이션용...)//JSDoc은 이렇게 사용한다.
/**
* @param { number } a
* @param { number } b
* @returns number
*/
export const hello = (a,b) => a + b
JSDoc은 API문서를 만들때 용이함!!
types
: 컴파일러가 참조할 (@types)패키지 목록을 명시적으로 제한satisfies
: 타입 단언을 보다 안전하게 사용할 수 있게해줌interface User{
name: string,
age: number
}
//안전하지 않은 타입 단언
const userB: User = {
name:"kim"
} as User
//안전한 타입 선언
const userB: User = {
//에러남
name:"kim"
}
//안전한 타입 단언(만족)
const userB: User = {
//에러남
name:"kim"
} satisfies User
// as는 안전하지 않다. 하지만 satisfies는 타입을 완벽히 만족해야함.
const userD = {
info:{
name: 'neo',
age:95,
isValid: true,
} satisfies User
}
js클래스, 내부 메소드에 JSDoc작성하고 천천히 TS로 옮긴다!
TS는 언뜻보면 쉽지만 실제로 적용하려면 애 먹는 종류일 것 같다.
토이프로젝트에 꼭 적용해봐야지...!!! 간단한 투두리스트라도 만들어봐야겠음.
마이그레이션도 언젠간 도전해보고싶지만 시간이...😥