자바스크립트는 타입 시스템이 없는 동적 프로그래밍 언어(=약한 타입 언어)
자바 스크립트의 변수는 문자열, 숫자 불린등 여러 타입의 값을 가질 수 있음.
타입 스크립트는 그런 자바스크립트에 강한 타입 시스템을 적용해 에러를 컴파일 환경에서 코드를 입력하는 동안 체크 할 수 있게 함.
타입 스크립트 확장자: .ts
타입 스크립트 작성 후 컴파일러를 사용해 자바 스크립트 파일로 컴파일해 사용할 수 있음.
VSCode에는 타입스크립트 기능이 내장돼있어, 별도 설정 없이도 .ts파일을 인식할 수 있음.
단, 컴파일러는 비포함이라 별도 설치해야함.
npm install typescript
컴파일러 일반 지역 설치:
$ npm install -D typescript
$ npx tsc --version
$ npx tsc ./src/index.ts
$ mkdir typescript-test
$ cd typescript-test
$ npm init -y
$ npm install -D typescript parcel-bundler
tsconfig.json 파일, main.ts 파일과 index.html 파일을 생성
//index.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>TypeScript Test</title>
</head>
<body>
<script src="main.ts"></script> // .js가 아닌 .ts 파일을 연결해야함.
</body>
</html>
마지막으로 터미널에 index.html을 지정하고 parcel 번들러로 빌드
$ npx parcel index.html
# Server running at http://localhost:1234
타입 지정
function add(a: number, b: number) {
return a + b;
}
const sum: number = add(1, 2);
console.log(sum); // 3
오류를 검색하기 쉽게 띄워줌 ex) ts2322
타입선언:
📌Boolean
//ture/flase 값을 나타냄
let isBoolean: boolean;
let isDone: boolean = false;
📌Number
//소수점, 2진수, 8진수 리터럴까지 지원
let num: number;
let integer: number = 6;
let float: number = 3.14;
let hex: number = 0xf00d; // 61453
let binary: number = 0b1010; // 10
let octal: number = 0o744; // 484
let infinity: number = Infinity;
let nan: number = NaN;
📌String
//따음표 ", ', ` 다 문제 없이 지원
let str: string;
let red: string = 'Red';
let green: string = "Green";
let myColor: string = `My color is ${red}.`;
let yourColor: string = 'Your color is' + green;
📌Array 배열
// 문자열만 가지는 배열
let fruits: string[] = ['Apple', 'Banana', 'Mango'];
// Or
let fruits: Array<string> = ['Apple', 'Banana', 'Mango'];
// 숫자만 가지는 배열
let oneToSeven: number[] = [1, 2, 3, 4, 5, 6, 7];
// Or
let oneToSeven: Array<number> = [1, 2, 3, 4, 5, 6, 7];
//문자와 숫자를 동시에 가지는 배열
let array: (string | number)[] = ['Apple', 1, 2, 'Banana', 'Mango', 3];
// Or
let array: Array<string | number> = ['Apple', 1, 2, 'Banana', 'Mango', 3
//배열이 가지는 항목을 단언할 수 없다면 any를 사용
let someArr: any[] = [0, 1, {}, [], 'str', false];
//인터페이스로 커스텀 파입을 사용
interface IUser {
name: string,
age: number,
isValid: boolean
}
let userArr: IUser[] = [
{
name: 'Neo',
age: 85,
isValid: true
},
{
name: 'Lewis',
age: 52,
isValid: false
}
];
//별쓸모는 업시만 특정값으로 타입을 대신할 수도 았긴 있음
let array = 10[];
array = [10];
array.push(10);
array.push(11); // Error - TS2345
//타입 선언 바로 앞에 readonly 키워드 나 ReadonlyArray 타입을 사용할 해서 읽기 전용으로 만들 수도 있음
let arrA: readonly number[] = [1, 2, 3, 4];
let arrB: ReadonlyArray<number> = [0, 9, 8, 7];
arrA[0] = 123; // Error - TS2542: Index signature in type 'readonly number[]' only permits reading.
arrA.push(123); // Error - TS2339: Property 'push' does not exist on type 'readonly number[]'.
arrB[0] = 123; // Error - TS2542: Index signature in type 'readonly number[]' only permits reading.
arrB.push(123); // Error - TS2339: Property 'push' does not exist on type 'readonly number[]'.
튜플과 이넘
튜플: 길이와 타입이 고정된 배열. 타입스크립트에만 존재하고 자바에는 존재하지 않음.
이넘: 열거형
Any: 모든 타입
Unknown: 알 수 없는 타입, 언노운에는 어떤 타입의 값도 할당할 수 있지만, 다른 타입에 언노운을 할당할 수는 없다.
Null/Undefined: 모든 타입의 하위 타입으로 서로의 타입에도 할당 가능
Void: 값을 반환하지 않는 함수서 사용
function hello(msg: string): void { //: void의 위치는 함수가 반환 타입을 명시하는 곳
console.log(`Hello ${msg}`);
}
Never: 절대 발생하지 않을 값, 어떤 타입도 적용할 수 없음.
Union: 2개 이상의 타입을 허용하는 경우, |를 사용해 타입을 구분하며 (괄호)는 선택 사항
let union: (string | number);
union = 'Hello type!';
union = 123;
union = false; // Error - TS2322: Type 'false' is not assignable to type 'string | number'.
인터섹션: &를 사용해 2개 이상의 타입을 조합하는 경우
// 기존 타입들이 조합 가능하다면 인터섹션을 활용할 수 있습니다.
interface IUser {
name: string,
age: number
}
interface IValidation {
isValid: boolean
}
const neo: IUser & IValidation = {
name: 'Neo',
age: 85,
isValid: true
};
함수: 화살표 함수를 이용해 타입을 지정, 인수의 타입과 반환값의 타입을 입력
// myFunc는 2개의 숫자 타입 인수를 가지고, 숫자 타입을 반환하는 함수.
let myFunc: (arg1: number, arg2: number) => number;
myFunc = function (x, y) {
return x + y;
};
// 인수가 없고, 반환도 없는 경우.
let yourFunc: () => void;
yourFunc = function () {
console.log('Hello world~');
};
//예시. 변수에 숫자 12를 할당해 숫자 타입으로 추론되었기 떄문에 string 타입의 값을 할당할 수 없기애 발생한 에러
let num = 12;
num = 'Hello type!'; // TS2322: Type '"Hello type!"' is not assignable to type 'number'.
// 타입 가드
// 매번 타입 단언을 사용하기 보다는, NAME is TYPE 형태의 타입 술부를 반환 타입으로 명시한 함수를 이용함.
//여기서 타입 술부는 val is number
function isNumber(val: string | number): val is number {
return typeof val === 'number';
}
function someFunc(val: string | number) {
if (isNumber(val)) {
val.toFixed(2);
isNaN(val);
} else {
val.split('');
val.toUpperCase();
val.length;
}
}
인터페이스
//인터페이스에서는 :이나 , 등의 기호를 사용하지 않아도됨.
interface IUser {
name: string,
age: number
}
// Or
interface IUser {
name: string;
age: number;
}
// Or
interface IUser {
name: string
age: number
}
선택적 속성으로 정의하는 법: 물음표를 사용한다.
interface IUser {
name: string,
age: number,
isAdult?: boolean // Optional property
}
// `isAdult`를 초기화하지 않아도 에러가 발생하지 않습니다.
let user: IUser = {
name: 'Neo',
age: 123
};
함수 타입
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');
인터페이스로 클래스르 정의하는 경우 implements를 사용
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 ICat {
name: string
}
class Cat implements ICat {
constructor(public name: string) {}
}
function makeKitten(c: ICat, n: string) {
return new c(n); // Error - TS2351: This expression is not constructable. Type 'ICat' has no construct signatures.
}
const kitten = makeKitten(Cat, 'Lucy');
console.log(kitten);
//ICat은 호출 가능한 구조가 아님.
//따라서 오류를 고치기 위해서는
//대신 new 키워드를 사용하고 ICatConstructor라는 호출 가능한 인터페이스를 정의
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);
인덱싱 기능 타입
interface IItem {
[itemIndex: number]: string | boolean | number[]
}
let item: IItem = ['Hello', false, [1, 2, 3]];
console.log(item[0]); // Hello
console.log(item[1]); // false
console.log(item[2]); // [1, 2, 3]
인터페이스도 클래스의 implements처럼 extend 키워드를 활용해 상속할 수 있음.
interface IAnimal {
name: string
}
interface ICat extends IAnimal {
meow(): string
}
타입별칭
조합한 타입뜰을 참조하는 별칭을 만드는 것
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
}
}
제네릭
//T는 타입변수고 타입변수는 매개 변수처럼 원하는 이름으로 지정할 수 있음.
function toArray<T>(a: T, b: T): T[] {
return [a, b];
}
toArray<number>(1, 2);
toArray<string>('1', '2');
toArray<string | number>(1, '2');
toArray<number>(1, '2'); // Error
// 타입변수 T가 string과 number인 경우만 허용해 extend를 사용한 제약 조건을 추가
T extends U
조건부 타입의 기본 문법
💡 조건부 타입(conditional types)
//extend 키워드
T extends U ? X : Y
//T 타입이 U에 할당가능한 타입이면 X라는 타입이고, 아니면 Y라는 타입
//infer 키워드
T extends infer U ? X : Y
// u가 추론 가능한 타입이면 참이고 아니면 거짓
this
const obj = {
a: 'Hello~',
b: function () {
console.log(this.a); // obj.a
// Inner function
function b() {
console.log(this.a); // global.a
}
}
};
//문제상황
obj.b(); // Hello~
const b = obj.b;
b(); // Cannot read property 'a' of undefined
function someFn(cb: any) {
cb();
}
someFn(obj.b); // Cannot read property 'a' of undefined
setTimeout(obj.b, 100); // undefined
//해결 1: 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~
// 해결 2: 화살표 함수
obj.b(); // Hello~
const b = () => obj.b();
b(); // Hello~
function someFn(cb: any) {
cb();
}
someFn(() => obj.b()); // Hello~
setTimeout(() => obj.b(), 100); // Hello~
오버로드
이름은 같지만 매개변수 타입과 반환 타입이 다른 여러 함수를 가질 수 있는 것
function add(a: string, b: string): string; // 함수 선언
function add(a: number, b: number): number; // 함수 선언
function add(a: any, b: any): any { // 함수 구현
return a + b;
}
add('hello ', 'world~');
add(1, 2);
add('hello ', 2); // Error - No overload matches this call.
클래스 바디란 {중괄호}로 묶여 있는 영역을 의미
클래스의 생성자 매소드constructor가 일반 메소드 멤버 class memebr과는 다르게 속성은 name: string; 아 같이 클래스 바디에 별도로 타입을 선언함.
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Cat extends Animal {
getName(): string {
return `Cat name is ${this.name}.`;
}
}
let cat: Cat;
cat = new Cat('Lucy');
console.log(cat.getName()); // Cat name is Lucy.
클래스 수식어
정적으로 사용되는 static과 읽기전용인 readonly는 위의 접근 제어자와 함께 사용할 수 있음.
추상클래스
타클래스가 파생될 수 있는 기본 클래스로 인터페이스와 유사
// Abstract Class
abstract class Animal {
abstract name: string; // 파생된 클래스에서 구현해야 합니다.
abstract getName(): string; // 파생된 클래스에서 구현해야 합니다.
}
class Cat extends Animal {
constructor(public name: string) {
super();
}
getName() {
return this.name;
}
}
new Animal(); // Error - TS2511: Cannot create an instance of an abstract class.
const cat = new Cat('Lucy');
console.log(cat.getName()); // Lucy
// Interface
interface IAnimal {
name: string;
getName(): string;
}
class Dog implements IAnimal {
constructor(public name: string) {}
getName() {
return this.name;
}
}
//추상 클래스와 인터페이스의 차이점은 속성, 메소드 멤버에 대한 세부 구현 가능여부임. 추상클래스는 가능함. 인터페이스는 불가능
매개변수
function add(x: number, y?: number): number {
return x + (y || 0);
}
const sum = add(2);
console.log(sum);
// ? 키워드의 사용은 | undefined를 추가 하는 것과 같음
function add(x: number, y: number | undefined): number {
return x + (y || 0);
}
const sum = add(2, undefined);
console.log(sum);
속성과 메소드
interface IUser {
name: string,
age: number,
isAdult?: boolean //isAdult를 선택적 속성으로 선언함
}
let user1: IUser = {
name: 'Neo',
age: 123,
isAdult: true
};
let user2: IUser = {
name: 'Evan',
age: 456
};```
페이닝: [참고자료](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Optional_chaining)
//str 속성이 undefined일 경우 toString 메소드를 사용 할 수 없어서 에러 발생.
obj?.prop;
obj?.[expr];
arr?.[index];
func?.(args);
// Error - TS2532: Object is possibly 'undefined'.
function toString(str: string | undefined) {
return str.toString();
}
// Type Assertion, str속성이 문자열이라는 것을 단언한 문제 해결법
function toString(str: string | undefined) {
return (str as string).toString();
}
// Optional Chaining ?.로 문제 해결
function toString(str: string | undefined) {
return str?.toString();
}