브라우저, 노드는 모두 js 파일을 실행한다
.언어이자 컴파일러(tsc)
이다. 컴파일러는 ts 코드를 js로 바꿔준다.npx tsc --init 시 생성
)에 따라 ts 코드를 js(tsc 시 생성)로 바꿔준다. 인풋인 ts와 아웃풋인 js 모두에 영향을 끼치므로 tsconfig.json 설정을 반드시 봐야한다.tsc --noEmit
하면 된다.esModuleInterop: true
, strict: true
두 개만 주로 켜놓는 편. strict: true가 핵심
임.결과물인 js를
실행해야 한다.간단한 타입은 타입스크립트가 알아서 타입을 추론해준다.
1.npm init -y
(:yes) (node 프로젝트 셋팅)
npm i(init) (-g:글로벌 설치 1번) typescript
npx tsc --init
(ts 설정파일 생성)npm
아니고 npx
주의
tsconfig.json 타입스크립트 설정파일 생성
"esModuleInterop": "true"
,"strict": "true"
(필수옵션)
forceConsistentCastingInfileNames: true
import someThing from '파일명' 문법 사용시 파일명의 대소문자를
엄격히 구분해서 import할 수 있도록,서도 다른 파일로 인식하도록 하는 옵션(=true)
윈도우에서는 구분을 안해서 true를 켜줘야하고, (filename.ts=Filename.ts)
리눅스&맥은 기본적으로 구분해줌.
=>true로 설정해두자
skiplibCheck: true
라이브러리 다운시 각 라이브러리의 타입을 정리해둔
d.ts
이 존재함,이에대한 타입체크를 건너 뛰어주세요~ 라는 옵션 ( 모든 라이브러리 타입 검사시 컴파일러가 느려질 수 있으므로 타입체킹을 건너 뛴다,실제로 쓰는 파일만 검사하도록 해주는게 좋음 )
npx tsc
npx tsc --noEmit
(컴파일과 출력은 별개의 과정임)tsc -w
옵션 켜두면 ts파일 저장시마다 자동으로 npx tsc
변수
,매개변수
,리턴값
에 단순히 타입을 지정할 뿐임.function add(x: number,y: number):number { return x + y }
// 함수 선언식
// `리턴값타입`의 자리는 `매개변수타입 바로 뒤`
const add1:(x:number, y: number) => number = (x,y) => x + y
// 함수표현식(화살표함수 할당)
// 함수타입을 변수명 옆에 할당(함수표현식 형태로)
변수
만들기: type aliasing
type Add = (x:number, y:number) => number;
const add: Add = (x,y) => x + y;
``
- Tips) ts는 js로 말이 되는 코드여야한다 (콜론: 뒤의 타입작성부를 지워보자)
## interface로도 타입 지정이 가능
```typescript
interface Add {
(x:number,y: number): number;
}
함수타입(파라미터:리턴값)
const obj: { lat: number, lon: number} = { lat: 37.6, lon: 127.6 }
const arr: string[] = ['123','456']
// 1.일반적인 배열타입, `속성[]`
const arr2: Array<number> = [123,456]
//2.꺽쇠(제네릭) 표기법
const arr3: [number, number, string] = [123,456,'hello']
function add(x:number, y:number): number; ///...타입만 미리 선언
function add(x,y) {
return x + y;
} ///... 타입선언부터 하고 함수 선언하기도 가능
as
키워드 앞의 타입을 강제로 특정 타입으로 변환시켜준다.let a = 123;
a = 'hello' as unknown as number
const head = document.querySelector('#head')!;
!
: non-null assertionif문으로 타입 내로잉하기
try {
const array:string[] = []; ///...빈배열[] 할당시 never타입
array.push('hello'); ///...string[]으로 타입지정해야 push메서드 사용가능
} catch (error) {
error;
}
type World = 'world';
Const a:World = 'world';
///...ctrl+shift : 자동완성추천
type Greeting = `hello ${World}`;
///...템플릿 리터럴 타입
function rest(...args: string[]) { ///args가 string[] 타입임을 의미
console.log(args); ///['1','2','3']
}
rest('1','2','3');
...args
는 args배열을 리스트화arguments
들의 배열이 된다.갯수 및 타입이 정해진
typeconst tuple: [ string, number] = ['1',1];
tuple[2] = 'hello'; ///... Error
tuple.push('hello') ///... 이건 ts가 못막아줌
const enum 변수
형태로 선언const enum EDirection {
Up = 123,
Down = 'How are you',
Left = 'say hi',
Right= 'enum',
}
const a = EDirection.Up;
const c = EDirection.Left;
enum 변수명
으로 선언enum
: 임의의 숫자나 문자열을 할당할 수 있으며, 하나의 그룹으로 묶고싶을 때 사용한다.BUT! enum은 ts의 문법이므로 트리쉐이킹이 되지 않는다 -> 번들링시 포함되므로 쓰지 않는 것이 좋다.
대신 Union type 사용을 권장 ( Union Type > const enum > enum )
Enum을 지양해야하는 이유
const ODirenction = {
Up: 0, ///... key값은 문자열 오버라이딩
Down: 1, ///...`as const`없다면 number로 기본 타입추론
Left: 2,
Right: 3,
} as const;
--아래와 같다--
const ODirection: {
readonly Up: 0;
readonly Down: 1;
readonly Left: 2;
readonly Right: 3;
}
- 객체선언 뒤에 `as const`붙이면 각 속성이 `readonly`인 `리터럴타입`을 생성해줌. // 각 속성에 값 자체로 타입이 할당됨.
## typeof
- `값`으로부터 타입을 만들고싶을 때 사용하는 연산자
## keyof
- `타입`의 key속성값만 뽑아서 새로운 타입으로 만들고싶을 때 사용
## keyof + typeof
```ts
type Direction = typeof ODirection[keyof typeof ODirection];
ODirection객체(값) '타입'을
typeof
으로 뽑아내서 타입생성하고, 해당 타입의 key속성값을keyof
뽑아내서
해당 타입으로 만들고 => 'Up'|'Down'|'Left'| 'Right' (type)
이것을 ODirection객체의 [computed property]형태로 참조
( ODirection[keyof typeof ODirection];)
// 1 | 2 | 3 | 4 (아직은 객체 참조값)
이것을 다시 typeof키워드로 타입으로 만들면
type Direction ///... 1 | 2 | 3 | 4
type A = { a :string };
const a: A = { a : 'hello' };
//const a:{ a: string } = { a : 'hello' };
interface B{ a : string };
const b: B = { a: 'hello'};
타입의 교집합타입을 만들어준다.
// 모든 속성이 다 있어야 함.
type A = stirng & number
///string,number를 모두 만족하는 타입은 존재할 수 없으므로 never타입
객체의 경우 속성을 합해준다
type B = { hello: 'world; } & { name : 'jake' }
const b: B = { hello: 'world', name: 'jake' };
Cf) { hello: 'world; } & { name : 'jake' } 와
{ hello: 'world; } | { name : 'jake' }는 다르다!
Intersection타입의경우 두 객체중 하나만 존재해도 만족이지만
Union타입의 경우 두 객체를 합친 타입이어야지만 만족한다.
type Animal = { breath : true };
type Mammal = Animal & { breed: true};
type Human = Mammal & { think: true };
interface A {
breath: true,
}
interface B extends A {
breed: true
} ///... A를 상속해서 B는 breath,breed 모든 속성을 갖는다.
const typeB: B = { breath: true, breed: true};
const jake: Human = { breath: true, breed:true, think: true}
interface newA {
talk: () => void;
}
interface newA {
eat: () => void;
}
interface newA {
shit: () => void;
}
const newA: newA = {
talk(){},
eat(){},
shit(){},
}
type A = { name: string };
type B = { age: number };
type newAB = A | B ;
// type newAB는 A와B보다 넓은 타입
type C = A & B ;
// type C는 A와,B보다 좁은 타입
const newC : C = { name : 'jake', age: 29};
// name,age키를 포함한 C는 더 상세하므로 좁은 타입
// 좁은타입은 넓은타입에 포함될 수 있다.
const ab: newAB = { name : 'jake' };
// const abc: C = { name: 'jake'}; (불가)
잉여속성검사
까지 진행하므로 에러가 뜰 수 있다. type C = { name: string } & { age: number };
const c: C = { name: string , age: number, married: false}
/// C타입에 존재하지 않는 married: false가 잉여속성 검사로인해 에러 발생.
void
면 리턴값이 존재하면안된다void
interface A {
talk: () => void;
}
const a: A ={
talk() { return 3; } ///에러가나지 않음, return을 무시
}
void
declare function forEach(arr: number[], callback: (el: number)=> void: void;
let target: number[] = [];
forEach([1,2,3], el => { target.push(el)});
forEach([1,2,3], el => target.push(el));
원래
function 변수명(파라미터타입):리턴타입
형태로 타입만 선언해두면
바로 밑에 함수선언을 해줘야 에러가 나지 않음.
하지만 declare키워드를 쓰면 타입선언만 해도 에러를 방지.
try{
} catch(error) { ///ts는 error를 unknown으로 기본 처리한다.
(error as AxiosError)
function numOrStr(a: number | string) {
if (typeof a === "number") {
a.toFixed(1);
} else {
a.charAt(3);
}
if(typeof a === 'string'){
a.charAt(3);
}
if(typeof a ==='boolean'){
a.toString();
}
}
numOrStr("123");
numOrStr(1);
Array.isArray(확인할배열값)
으로 타입가드작성function numOrNumArray(a: number | number[]){
if(Array.isArray(a)){ //number[] }
a.concat(4)
} else { //number
a.toFixed(3);
}}
인스턴스의
타입이된다.class A {
aaa()
}
class B {
bbb()
}
function aOr B(param: A | B){
///...param은 A or B의 인스턴스이므로 클래스를 타입으로 가질 수 있음
if( param instanceof A){ ///... param이 A의 인스턴스일 때
param.aaa(); ///A메서드 호출가능.
}
}
aOrB(new A());
aOrB(new B());
속성의 값
으로도 타입을 체크type B = { type: 'b', bbb: string};
type C = { type: 'c', ccc: string};
type D = [ type: 'd', ddd: string};
function typeCheck( a: B | C | D){
if(a.type === 'b'){
a.bbb;
} else if (a.type === 'c'){
a.ccc;
} else {
a.ddd;
}
in
연산자: 객체속성명으로 타입 구분하기type B = { type: 'b', bbb: string};
type C = { type: 'c', ccc: string};
type D = [ type: 'd', ddd: string};
function typeCheck( a: B | C | D){
if( bbb in a ){ /// bbb속성이 a파라미터에 있다면? => B로추론
a.type;
} else if (a.type === 'c'){
a.ccc;
} else {
a.ddd;
}
# 커스텀 타입 가드
- 타입을 구분해주는 `커스텀 타입가드 함수`를 작성 가능.
```ts
function catOrDog(a: Cat | Dog): a is Dog{
if((a as Cat).meow { return false }
return true;
}
typeof
,instanceof
,in
,Array.isArray
로 구분하기 힘든 경우 사용function pet(a: Cat | Dog){
if(catOrDog(a)){
console.log(a.bow);
}
if('meow' in a) {
console.log(a.meow);
}
}
# ts 4.8ver 업데이트 내용
```ts
const x: {} = 'hello';
const y: Object = 'hi'; //대문자Object
// {}, Object는 모든 타입 할당 가능을 의미(null,undefined제외)
const yy: object = { hello:'world' };
//하지만 object는 지양, interface나type,class 사용 권장
const z: unknown = 'hi'
//unknown: {}(모든타입)| null | undefined의 합성
interface A {
readonly a: string;
b: string
}
const aaaa: A = { a:'hello', b: 'world'};
aaaa.a = '123'; ///readonly로 인해 재할당 불가
type A = { a: string , b: string ,c: string ,d : string}; //너무 길다..
//위,아래는 동치
type indexedA = { [key: string]: string}
// 객체의 어떤key값이던 전부 string이고 그 값도 string이다.를 의미
type BB = 'Human' | 'Mammal' | 'Animal';
type newB = { [key in BB]: number }
// key값은 BB타입의 key값중에서 ,값은 number로 ..
const newB = { Human: 123, Mammal: 5, Animal: 7}
type newC = { [key in BB]: B };
class A {
a: string;
b: number; //인스턴스의 프로퍼티가 되는 값은 타입초기화 필요
constructor(a: string, b:number =123){ // 123: default value
this.a = a;
this.b = b;
}
class A {
a: string = '123'
b: number = 123 ///... 인스턴스에 복제된다.
}
class newA2{
public method(){ const aaaa = 'bb'}
public props = 123;
}
const insA2 = new newA2();
insA2.method()
insA2.props
class B implements A(인터페이스)
public protected private
클래스내부사용 O O O
인스턴스사용 O X X
상속클래스사용 O O X
abstract class DB { ///... 클래스의 모양만 구현해놓는 abtract 추상클래스
private readonly a: string = '123';
b: string = 'world';
c: string = 'wow';
abstract method(): void; ///...반드시 구현해줘야하는 method(abstract)
method2(){
return '3';
}
}
class DBD extends DB {
method(){}; ///...abtract가 붙은 method는 필수로 구현
}
?
function abc( a: number, b?: number, c?:number){}
abc(1)
abc(1,2)
abc(1,2,3)
let obj: { a: string, b?:string } = { a: 'hello', b:'world', c:'wow' }
obj = { a: 'hello' }
변수명 뒤 < >
로 표현한다.function add<T>(x: T, y:T):T {
return x + y;
}
add(1,2) /// T: number로 확정
add('1','2') ///T: string으로 확정
왼쪽을 오른쪽으로 제한
의 의미를 갖는다.function add<T extends number>()
<T extends number, K extends string>
- 콜백함수의 형태를 제한
function add<T extends (a:string) => number>
add(a => +a)
add<T extends (...args:any => any)>
add<T extends abstract new (...args: any)=> any>
cosnt a = (b: number = 3, c:number =4) => { return '3';}
const a = (b: { children: string} = { children: 'zerocho'}) => { }
const add = <T = unknown>(x: T, y: T) = ({ x, y})
1.기본값부여 T = unknown
, 2. T extends unknown
3. <T,> (의도가 불분명해서 비권장)
로 에러체크를 피한다.