- Published on
Typescript 정리
- 글쓴이
📌 목차
- Intro
- Type Basic
- Interface
- Type Alias
- Generic
- Function
Intro
타입스트립트 사용법
- 자바스크립트 파일의 확장자가
.js
인것처럼, 타입스트립트 파일의 확장자는.ts
이다. tsc
명령어를 통해 타입스크립트 컴파일러를 별도로 사용해.js
파일로 변환한다.npm install -g typescript
: 글로벌로 타입스크립트 설치
- 코드검사, 수정 등을 도와주는 에디터를 사용하는 것이 편리하다.
ex) vscode, webstorm
타입스크립트의 기능
- 자바스크립트의 수퍼셋으로의 기능을 가지고 있다.
- 객체지향과 정적타입 문법을 지원한다.
타입스크립트 프로젝트세팅
- 타입스크립트 컴파일 옵션을
tsconfig.json
파일을 통해 관리할 수 있다. - 해당 파일을 프로젝트 루트 경로에 생성하여 프로젝트별로 컴파일 옵션을 관리할 수 있다.
- https://www.typescriptlang.org/play 에서 타입스크립트 코드가 자바스크립트언어로 어떻게 변환되는지 확인할 수 있다.
- https://replit.com/new/typescript 에서 간단한 타입스크립트 프로젝트를 생성할 수 있다.
로컬에서 프로젝트세팅하기
- 로컬에서는 프로젝트 폴더 하위에
npm install -D typescript parcel-bundler
를 추가하고,tsconfig.json
를 만들어 사용하면 편리하다. tsconfig.json
예시
{
"compilerOptions": {
"strict": true,
"target": "ES6",
"lib": ["ES2015", "DOM"],
"module": "CommonJS"
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules"
]
}
- 로컬 프로젝트 예시 구조
├── index.html
├── package-lock.json
├── package.json
├── src
│ └── main.ts
└── tsconfig.json
main.ts
에 타입 스크립트 코드를 작성하고,index.html
에서 타입스크립트를 참조시킨다.npx parcel index.html
을 통해 빌드하면 로컬에서 돌아간다.
Type Basic
타입지정 기본
변수 오른쪽에 : type_name
과 같은 형태로 타입을 명시한다.
const num : number = 1;
const num : number = "1" // ERROR!! Type 'string' is not assignable to type 'number'.
const str : string = "string"
배열은 다음과 같이 타입을 선언할 수 있다.
case 1: 하나의 원시타입을 원소로 가지는 배열
const strArr : string[] = ['a','b','c'];
const strArr2 : Array<string> = ['a','b','c'];
case 2: 두개의 원시타입을 원소로 가질 수 있는 배열
|
연산자로 타입을 연결시킨 것을 union type이라고 하며, or 조건과 같이 두개의 타입을 모두 허용한다.
const arr : (string : number)[] = ['a','b','c',1,2,3];
case 3: 모든 타입을 허용하는 배열
- 배열의 타입을 단언할 수 없을 때 사용한다.
const anyArr : any[] = ['a',true,1];
case 4 : interface나 user-define type을 원소로 가지는 배열
interface User {
id : string,
name : string,
isAdmin : boolean
}
const userArr : User[] = [
{
id: 'fjdkfj',
name : "Tina",
isAdmin : true
}
]
튜플 타입지정
- 튜플은 고정된 길이의 배열을 표현한다.
- 고정된 길이의 배열이기 때문에 특정인덱스에 위치하는 타입을 명시할 수 있다.
- 재할당할수 없다.(assign x)
- push, spllice는 가능하다.
let users [string, string ,boolean][];
user = [['1','Tina',true],['2','Lion',false]];
enum 타입지정
- 매핑값은 0부터 시작된다.
- 문자열 매핑해서 사용할 수 있지만, 개별로 매핑해야 한다.
enum Week {
Mon,
Tue,
Wed,
Thu,
Fri,
Sat,
Sun
}
enum Size {
S = 'small',
M = 'medium',
L = 'large'
}
any, unknown 타입지정
- any로 타입을 지정하면 바닐라 js에서 변수를 할당하는 것과 동일하게 사용할 수 있다.
- unknown으로 타입을 지정하면 나머지는 any와 동일하지만 해당 변수를 다른 타입인 변수에 대입할수 없다.
as type_name
으로 타입을 특정하면 가능. - unknown보단 보다 명확한 타입을 사용하는 것이 좋다.
const anyNum : any = 1
let num = anyNum;
const unknownNum : unknown = 1
num = unknownNum // error
num = unknownNum as number
object 타입 지정
- object는 최상위 객체를 포함한 모든 객체를 의미하기 때문에 요소의 타입을 지정하거나 interface를 사용한다.
let obj : object = null;
// type
let user1 : {id:string, name:string, isAdmin:boolean} = {
id: 'fjdkfj',
name : "Tina",
isAdmin : true
}
//interface
interface User {
id : string,
name : string,
isAdmin : boolean
}
함수
- void 로 반환값이 없는 함수로 명시할 수 있다.
- never는 항상오류는 출력하거나 어떤값도 반환하지 않을때 사용한다.
- 화살표 함수를 통해 타입을 지정할 수 있다.
let myFunc : () => void;
union, intersection
|
연산자로 타입을 연결시킨 것을 union type이라고 하며, or 조건과 같이 두개의 타입을 모두 허용한다.&
연산자로 타입을 두개다 포함시킬 수 있다. 새로운 타입을 생성하지 않고 기존의 타입을 조합해 사용하는 방법이다.
타입추론
- typescript에서 모든 상황에 타입을 명시해야 하는 것은 아니다.
- 타입이 명확해, 타입을 추론해주는 경우에는 프로그래머가 타입을 명시할 필요가 없다.
- 초기화된 변수
- 기본값이 설정된 함수의 매개변수
- 반환값이 있는 함수
타입단언
- 말그대로 프로그래머가 타입에 대해 확신을 가지고 단언하는 것이다.
- 함수의 로직은 숫자만 받아들이지만, 문자열도 숫자로 변환해 사용하고 싶은 경우에 사용한다.
- 프로그래머가 타입스트립트에게 타입에 대한 힌트를 주는 것이다.
- 타입단언을 통해 컴파일 타임에서 타입에 대한 처리를 마무리할 수 있다.
<Type>
이나as Type
을 통해 타입을 단언할 수 있으며, 전자는 jsx문법과 충돌이 발생하기도 한다.
타입가드
- 타입단언을 여러번 사용해야 하는 경우등 일정한 범위의 타입을 단언할때 타입가드를 통해 처리한다
is
,typeof
,in
,instanceof
등의 연산자를 사용해 타입가드를 처리할 수 있다.
Interface
- interface 키워드를 통해 클래스의 스펙을 정의하거나, 객체로서 활용할 수 있다.
- 멤버변수를 구분할때,
,
,;
로 구분하거나 개행으로만 구분할 수 있다. - 변수명 뒤에
?
를 붙여 옵셔널한 변수를 선언할수도 있다. - 변수명 앞에
readonly
를 붙여 할당뒤에 값 수정을 방지할 수 있다. - 모든 멤버가
readonly
라면 유틸리티나 타입단언을 사용해 문법적으로 간결하게 작성할 수 있다.
interface IUser {
name: string,
age: number
}
// Or
interface IUser {
name: string;
age: number;
}
// Or
interface IUser {
name: string
age: number
}
interface IUser {
name: string
readonly age: number
isAdmin? : boolean
}
// Readonly Utility
interface IUser {
name: string,
age: number
}
let user: Readonly<IUser> = {
name: 'Neo',
age: 36
};
user.age = 85; // Error
user.name = 'Evan'; // Error
// Type assertion
let user = {
name: 'Neo',
age: 36
} as const;
user.age = 85; // Error
user.name = 'Evan'; // Error
함수 in interface
- 함수의 시그니처를 인터페이스에 선언할 수 있다.
interface IName {
(PARAMETER: PARAM_TYPE): RETURN_TYPE // Call signature
}
interface IUser {
name: string
}
interface IGetUser {
(name: string): IUser
}
const getUser: IGetUser = function (n) { // n is name: string
// Find user logic..
// ...
return user;
};
getUser('Heropy');
클래스 of interface
- interface의 정해진 시그니처 형태로 구현하기 위해 interface를 상속받을수 있다.
interface IUser {
name: string,
getName(): string
}
class User implements IUser {
constructor(public name: string) {}
getName() {
return this.name;
}
}
const neo = new User('Neo');
neo.getName(); // Neo
- interface는
extends
키워드를 통해 interface 간의 상속이 가능하다.
interface IAnimal {
name: string
}
interface ICat extends IAnimal {
meow(): string
}
class Cat implements ICat { // Error - TS2420: Class 'Cat' incorrectly implements interface 'ICat'. Property 'name' is missing in type 'Cat' but required in type 'ICat'.
meow() {
return 'MEOW~'
}
}
구성 시그니처
- interface 형태의 객체를 호출하려면
new
키워드를 사용해 구성 시그니처를 선언해야 한다.
// 구성 시그니처
interface ICat {
name: string
}
interface ICatConstructor {
new (name: string): ICat;
}
class Cat implements ICat {
constructor(public name: string) {}
}
function makeKitten(c: ICatConstructor, n: string) {
return new c(n); // ok
}
const kitten = makeKitten(Cat, 'Lucy');
console.log(kitten);
indexable type
- 숫자나 문자열 형태로 접근이 가능한 타입을 indexable type이라고 한다.
- interface에서도 해당 feature를 이용할 수 있다.
interface IUser {
[userProp: string]: string | boolean
}
let user: IUser = {
name: 'Neo',
email: 'tinajeong@gmail.com',
isValid: true,
0: false
};
console.log(user['name']); // 'Neo' is string.
console.log(user['email']); // 'tinajeong@gmail.com' is string.
console.log(user['isValid']); // true is boolean.
console.log(user[0]); // false is boolean
console.log(user[1]); // undefined
console.log(user['0']); // false is boolean
Type Alias
type
키워드를 사용해 타입의 별칭을 만들수 있다. 일종의 user-defined 타입이다.
type MyType = string;
type YourType = string | number | boolean;
type TUser = {
name: string,
age: number,
isValid: boolean
} | [string, number, boolean];
let userA: TUser = {
name: 'Neo',
age: 85,
isValid: true
};
let userB: TUser = ['Evan', 36, false];
function someFunc(arg: MyType): YourType {
switch (arg) {
case 's':
return arg.toString(); // string
case 'n':
return parseInt(arg); // number
default:
return true; // boolean
}
}
Generic
- 타입을 선언 시점이 아닌, 사용하는 시점에 타입을 명시할 수 있는 문법이다.
function toArray<T>(a: T, b: T): T[] {
return [a, b];
}
toArray<number>(1, 2);
toArray(1, 2); // 타입추론
toArray<string>('1', '2');
toArray<string | number>(1, '2');
toArray<number>(1, '2'); // Error
- 제약조건을 넣어 일부타입으로 제너릭 사용을 제한 시킬수도 있다.
- 3개 이상의 타입을 명시하려면
type
키워드를 사용한다. - 제약조건은
extends
와 함께만 사용될 수 있다.
interface MyType<T extends string | number> {
name: string,
value: T
}
type U = string | number | boolean
interface IUser<T extends U> {
name: string,
age: T
}
Function
this, bind
- callback 함수등 호출하지 않은 함수가 있을 경우에는 bind를 이용해 매핑해준다.
obj.b(); // Hello~
const b = obj.b.bind(obj);
b(); // Hello~
function someFn(cb: any) {
cb();
}
someFn(obj.b.bind(obj)); // Hello~
setTimeout(obj.b.bind(obj), 100); // Hello~