타입스크립트는 일반 변수, 매개 변수, 객체 속성 등에: TYPE 같은 형태로 타입을 지정할 수 있다.
let a: string = 'text'; // 문자열
let b: number = 0; // 숫자형
let c: boolean = true; // 논리형
let d: any = true; // 어떤 타입이 올지 모를 때
let e: string | number = '0'; // 문자열이나 숫자가 올 때
만약 아래와 같이 타입을 선언하고 다른 타입의 값을 대입하거나 값이 선언된 후에 다른 타입의 값이 대입되면 에러를 발생한다.
// 문자열로 선언하고 숫자를 대입하면 에러 발생
let a: string = 0; // error
// 문자열 값이 들어가고 이후에 숫자가 대입하면 에러 발생
let b: string = 'text';
b = 1; // error Type '1' is not assignable to type 'string'. TS2322
타입스크립트는 ES5, ES6의 상위 확장인 언어이므로 자바스크립트의 타입을 그대로 사용하며, 이외에도 타입스크립트의 고유의 타입이 추가로 제공된다.
Type | JS | TS | Description |
---|---|---|---|
boolean | ◯ | ◯ | true와 false |
null | ◯ | ◯ | 값이 없다는 것을 명시 |
undefined | ◯ | ◯ | 값을 할당하지 않은 변수의 초기값 |
number | ◯ | ◯ | 숫자(정수와 실수, Infinity, NaN) |
string | ◯ | ◯ | 문자열 |
symbol | ◯ | ◯ | 고유하고 수정 불가능한 데이터 타입이며 주로 객체 프로퍼티들의 식별자로 사용(ES6에서 추가) |
object | ◯ | ◯ | 객체형(참조형) |
array | ◯ | 배열 | |
tuple | ◯ | 고정된 요소수 만큼의 타입을 미리 선언후 배열을 표현 | |
enum | ◯ | 열거형. 숫자값 집합에 이름을 지정한 것 | |
any | ◯ | 타입 추론(type inference)할 수 없거나 타입 체크가 필요없는 변수에 사용 | |
void | ◯ | 일반적으로 함수에서 반환값이 없을 경우 사용 | |
never | ◯ | 결코 발생하지 않는 값 |
let bool01: boolean = true;
let bool02: boolean = false;
let num01: number = 5;
let num02: number = 3.14;
let num03: number = NaN;
ES6의 템플릿 문자열도 지원한다.
let str01: string = 'text';
let str02: string = `my name is ${val}`;
배열(Array)
배열은 아래와 같이 두가지 방법으로 타입을 선언할 수 있다.
// 문자열만 가지는 배열
let arr01: string[] = ['a', 'b', 'c'];
let arr02: Array<string> = ['a', 'b', 'c'];
// 숫자형만 가지는 배열
let arr03: number[] = [1, 2, 3];
let arr04: Array<number> = [1, 2, 3];
// Union 타입(다중 타입)의 문자열과 숫자를 동시에 가지는 배열
let arr05: (string | number)[] = [1, 'a', 2, 'b', 'c', 3];
let arr06: Array<string | number> = [1, 'a', 2, 'b', 'c', 3];
// 배열이 가지는 값의 타입을 추측할 수 없을 때 any를 사용할 수 있다.
let arr07: (any)[] = [1, 'a', 2, 'b', 'c', 3];
let arr08: Array<any> = [1, 'a', 2, 'b', 'c', 3];
배열이 가지는 항목의 값을 단언할 수 없다면 any를 사용할 수 있다.
let someArr: any[] = [0, 1, {}, [], "str", false];
함수(Function)
함수는 파라미터에 각각 타입을 선언해 주며, 파라미터 우측에는 해당 함수의 리턴값 타입도 선언해 주면 된다.
function sum(a: number, b: number): number {
return a + b;
}
console.log(sum(2, 3)); // 5
리턴값을 숫자형으로 선언하였는데 다른 값이 리턴된다면 역시 에러가 난다.
function sum(a: number, b: number): number {
return null; // error
}
기본적으로 typeof 연산자가 object로 반환하는 모든 타입을 나타낸다. 여러 타입의 상위 타입이기 때문에 그다지 유용하지 않다.
let obj: object = {};
let arr: object = [];
let func: object = function() {};
let date: object = new Date();
보다 정확하게 타입 지정을 하기 위해 아래와 같이 객체 속성들에 대한 타입을 개별적으로 지정할 수 있다.
let user: { name: string, age: number } = {
name: 'a',
age: 20
};
console.log(user); // {name: "a", age: 20}
type PositionX = { x: number };
type PositionY = { y: number };
type XandY = PositionX & PositionY
let 좌표 :XandY = { x : 1, y : 2 }
인터페이스(Interface)나 커스텀 타입을 사용할 수도 있다.
interface IUser {
name: string;
age: number;
isVaild: boolean;
}
let userArr: IUser[] = [
{
name: "Neo",
age: 10,
isVaild: true,
},
{
name: "Lewis",
age: 64,
isVaild: false,
},
{
name: "Evan",
age: 123,
isVaild: true,
},
];
읽기 전용 배열을 생성할 수 있다. readonly ReadonlyArray 타입 사용
let arrA: readonly number[] = [1, 2, 3, 4];
let arrB: ReadonlyArray<number> = [2, 4, 6, 8];
arrA[0] = 123;
배열과 유사하다. 차이점은 정해진 타입의 요소 개수 만큼의 타입을 미리 선언후 배열을 표현한다.
let tuple: [string, number];
tuple = ['a', 0];
console.log(tuple); // ["a", 0]
정해진 요소의 순서 및 개수가 다르면 오류를 출력한다.
let tuple: [string, number];
tuple = [0, 'a']; // error
tuple = ['a', 0, 1]; // error
아래와 같이 데이터를 개별 변수로 지정하지 않고, 단일 튜플 타입으로 지정해 사용할 수 있다.
// 기존 변수
let name: string = 'a';
let age: number = 20;
// 튜플
let user: [string, number] = ['a', 20];
console.log(user); // ["a", 20]
값으로 타입을 대신할 수도 있다. 처음 선언할 때의 값과 다른 값이 할당 되면 에러가 출력된다.
let user: ['a', number];
user = ['a', 20]; // ["a", 20]
user = ['a', 30]; // ["a", 30]
user = ['b', 30]; // error
튜플은 정해진 타입과 고정된 길이의 배열이지만, 값을 할당할 때만 해당된다. push나 splice같은 메소드를 통해 값을 넣는건 막을 수 없다.
let user: [string, number];
user = ['a', 20]
console.log(user); // ["a", 20]
user.push(30);
console.log(user); // ["a", 20, 30]
숫자 혹은 문자열 값 집합에 이름을 부여할 수 있는 타입으로, 값의 종류가 일정한 범위로 정해져 있는 경우 유용하다. 기본적으로 0부터 시작하며, 값은 1씩 증가한다.
enum obj {
a,
b,
c,
d,
e
}
console.log(obj);
// 0: "a"
// 1: "b"
// 2: "c"
// 3: "d"
// 4: "e"
// a: 0
// b: 1
// c: 2
// d: 3
// e: 4
수동으로 값을 변경할 수 있으며, 변경한 부분부터 다시 1씩 증가한다.
enum obj {
a,
b = 10,
c,
d,
e
}
console.log(obj.b); // 10
console.log(obj.c); // 11
Any는 모든 타입을 의미하며, 기존의 자바스크립트 변수와 마찬가지로 어떠한 타입의 값도 할당할 수 있다. 불가피하게 타입을 선언할 수 없는 경우, 유용할 수 있다.
let any:any = 'String';
any = 0;
console.log(any); // 0
any = true;
console.log(any); // true
빈 타입인 Void는 리턴값이 없는 함수에서 사용된다. 리턴값의 타입을 명시하는 곳에 작성하며, 리턴값이 없는 함수는 undefined를 반환한다.
function hello(): void {
console.log("hello");
}
console.log(hello()); // undefined
any와 같이 최상위 타입인 unknown은 알 수 없는 타입을 의미한다.
어떤 타입의 값도 할당할 수 있지만, unknown을 다른 타입에 할당할 수 없다.
let u: unknown = 123;
let test1: number = u; //알 수 없는 타입(unknown)은 모든 타입(any)을 제외한 다른 타입에 할당할 수 없습니다.
let test2: number = u as number; //타입을 단언하면 할당할 수 있다.
let test3: any = u; // any와 unknown은 서로 할당할 수 있다.
기본적으로 null, undefined는 모든 타입의 하위 타입으로, 각 타입에 할당할 수 있다.
서로의 타입에도 할당 가능하다.
let num: number = undefined;
let str: string = null;
let obj: { a: 1, b: false } = undefined;
let arr: any[] = null;
let und: undefined = null;
let nul: null = undefined;
let voi: void = null;
컴파일 옵션에서 "strictNullChecks": true를 통해 엄격하게 Null과 undefined 서로의 타입까지 할당 할 수 없게 한다. 단, void에는 undefined할당 가능
let vod: void = undefined;
Never 타입은 절대 발생할 수 없는 타입을 나타낸다.
function errorMsg() {
throw new Error("오류 발생!");
}
console.log(errorMsg()); // Uncaught Error: 오류 발생!
errorMsgd 함수는 오류를 발생시키기 때문에 null, undefined를 포함한 어떠한 값도 반환하지 않는다. 이럴 경우 never 타입을 사용하면 된다.
2개 이상의 타입을 허용하는 경우, 이를 유니언이라 한다.
| (vertical bar)를 이용해 타입을 구분하며, ()은 선택 사항.
let union: (string | number);
union = 'Hello World';
union = 777;
union = false; // Error - TS2322: Type 'false' is not assignable to type 'string | number'.
// string, number 타입만 허용하기 때문에 에러.
&(ampersand)를 사용해 2개 이상의 타입을 조합하는 경우, 이를 인터섹션이라고 한다
인터섹션은 새로운 타입을 생성하지 않고 기존의 타입들을 조합할 수 있기 때문에 유용하지만 자주 사용되지는 않는다.
interface User {
name: string,
age: number
}
interface Validation {
isValid: boolean
}
const testCase: User = {
name: 'juyoung',
age: 27,
isValid: true // Error - TS2322: Type '{ name: string; age: number; isValid: boolean; }' is not assignable to type 'User'.
};
const testCase2: user & Validation = {
name: 'jisu',
age: 30,
isValid: true
}
자바에서 인터페이스는 클래스를 구현하기 전에 필요한 메서드를 정의하는 용도로 쓰이지만, 타입스크립트에서는 좀 더 다양한 것들을 정의하는 데 사용된다.
인터페이스로 타입을 정의할 때는 interface 키워드를 사용한다.
interface Person {
name: string;
age: number
}
const p1: Person = { name: 'mike', age: 23};
const p2: Person = { name: 'mike', age: 'ten'};
Person 인터페이스를 정의하고 객체 내부에 존재하는 각 속성의 타입을 정의한다. p2는 age가 number 여야 되는데 string을 넣어서 타입 에러가 났다.
interface Person {
name: string;
age?: number;
}
const p1: Person = { name: 'mike' };
? 기호를 붙이면 선택 속성으로 할 수 있다.
interface Person {
readonly name: string;
age?: number;
}
const p1: Person = {
name: 'mike',
}
p1.name = 'jone'; // 컴파일 에러
정의 되지 않은 속성값을 갖고 있어도 할당이 가능하다. 단, 리터럴로 값을 초기화하는 경우에는 인터페이스에 정의되지 않은 속성값이 있으면 타입에러가 발생한다.
리드온리는 속성변경불가
interface Person {
readonly name: string;
age?: number;
}
const p1: Person = { // Person에 birthday가 없으므로 에러가 난다.
name: 'mike',
birthday: '1997-01-01',
}; // 타입에러
const p2 = {
name: 'mike',
birthday: '1997-01-01',
};
const p3: Person = p2; // 에러가 안 난다. p3타입이 p2타입을 포함하는 더 큰 타입이기 때문이다.
interface Name= string
interface Name= number
interface Student {
name :string,
}
interface Teacher {
name :string,
age :number,
}
//중복되는거 extends 해줌 (class랑 똑같음)
interface Student {
name :string,
}
interface Teacher extends Student {
age :number
}
//type alias경우 이런식으로
type Animal = {
name :string
}
type Cat = Animal & { legs: number }
interface Animal {
name :string
}
interface Animal {
legs :number
}
interface의 경우 타입이름 중복선언을 허용해주며
중복시 extends 한 것이랑 동일하게 동작합니다.
이러면 Animal 타입은 name, legs 속성을 가질 수 있습니다.
type Animal = {
name :string
}
type Animal = {
legs :number
}
type의 경우 중복선언을 허용하지 않습니다. 에러남
interface Animal {
name :string
}
interface Dog extends Animal {
name :number
}
Animal을 복사해서 Dog interface를 만들어봤습니다.
근데 name 속성이 중복되네요? 그럼 에러납니다
interface Animal {
name :string
}
interface Dog {
name :number
}
let 변수 :Dog & Animal = { name : '멍멍' }
근데 name 속성이 중복되네요? 그럼 에러납니다
interface 말고도 type 키워드도 똑같은 현상이 일어납니다.
(주의) 근데 name : string , name : number 라서 에러가 나는 것이지
둘다 name : string 타입이면 에러가 나지 않습니다. 하나로 합쳐줌
타입 별칭은 타입의 새로운 이름을 생성한다. 프로그래머의 의도를 보다 명확하게 나타낼 수 있다.
custom type이라고 부르기도한다.
기본 타입을 포함한 모드타입이 올 수 있다.
type Name = string;
type Age = number;
type CombineNumberString = string | number;
type User = {
name: string;
heihgt: number;
}
type NumOut = (x : number, y : number ) => number ;
let ABC : NumOut = function(x,y){
return x + y
methods type aliases
type Member = {
name : string,
age : number,
plusOne : ( x :number ) => number,
changeName : () => void
}
let 회원정보:Member = {
name : 'kim',
age : 30,
plusOne (x){
return x + 1
},
changeName : () => {
console.log('안녕')
}
}
어떤 속성이 들어갈지 아직 모른다면
그냥 전부 싸잡아서 타입지정도 가능합니다.
interface IPerson {
[key: string]: string;
}
const person: IPerson = {};
person['name'] = 'Yeonju'; //ok!!
const foo: string = 'foo';
person[foo] = 'hello'; //ok!!
TypeScript에는 문자열과 숫자, boolean 세 가지 리터럴 타입이 있는데 이를 사용하면 문자열이나 숫자에 정확한 값을 지정할 수 있습니다.
const로 변수를 선언하게 되면 TypeScript에게 이 객체는 절대 변경되지 않음을 알립니다.
const helloWorld = "Hello World";
let hiWorld = "Hi World";
위 예시에서 helloWorld는 문자열이 아닌 "Hello World"로 타입을 정하지만 hiWorld는 변경될 수 있으므로 문자열로 선언합니다.
이렇게 잠재적 케이스의 수를 줄이는 것을 Narrowing 이라고 부릅니다.
문자열 리터럴 타입은 Union types, type guards, type aliases과 잘 결합됩니다. 이런 기능을 통해 문자열을 enum처럼 사용할 수 있습니다.
type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
animate(dx: number, dy: number, easing: Easing) {
if (easing === "ease-in") {
// ...
} else if (easing === "ease-out") {
} else if (easing === "ease-in-out") {
} else {
// 하지만 누군가가 타입을 무시하게 된다면
// 이곳에 도달하게 될 수 있습니다.
}
}
}
let button = new UIElement();
button.animate(0, 0, "ease-in");
button.animate(0, 0, "uneasy");
Typescript에는 숫자형 리터럴 타입도 있습니다.
function rollDice(): 1 | 2 | 3 | 4 | 5 | 6 {
return (Math.floor(Math.random() * 6) + 1) as 1 | 2 | 3 | 4 | 5 | 6;
}
interface ValidationSuccess {
isValid: true;
reason: null;
};
interface ValidationFailure {
isValid: false;
reason: string;
};
type ValidationResult =
| ValidationSuccess
| ValidationFailure;