테스트 사이트 : https://www.typescriptlang.org/play?#code/Q
TypeScript | JavaScript |
---|---|
static | dynamics |
개발 도중, 에러 확인 가능 | 런타임 할 때가 돼서야 에러 확인 가능 |
//javascript
function add(n1, n2) {
if (typeof n1 !== "number" || typeof n2 !== "number") {
throw new Error("Incorrect input!"); // 런타임시 에러를 감지
}
return n1 + n2;
}
const result = add(39, 28);
// typescript
function add1(n1: number, n2: number) {
// 개발 환경에서 에러를 체크해주기 때문에 런타임시 에러 감지를 위한 코드를 작성할 필요가 없다.
return n1 + n2;
}
ECMA 표준에 따른 기본 자료형 | 타입스크립트 추가 자료형 |
---|---|
Boolean, Number, String, Null, Undefined, Symbol, Array: object | Any, Void, Never, Unknown, Enum, Tuple : object |
Object와 Reference가 아닌 실제 값을 저장하는 자료형.
ex) Boolean / Number / String / Null / Undefined / Symbol
리터럴 표기로 타입을 나타낼 수 있다.
let a = "Hi";
new 생성자를 이용해 wrapper object로 만들어 참조형 자료들처럼 사용할 수 있다.
이렇게 만들어진 wrapper object는 primitive type이 아니다.
const str = "hodu";
const strObj = new String(str); // 문자열 객체 생성
str.length; // 리터럴 값은 내부적으로 wrapper object 를 생성한 후, length Property를 참조한다.
str == strObj; // 동등 연산자는 리터럴 값과 해당 wrapper object를 동일하게 본다.
str === strObj; // 일치 연산자는 리터럴 값과 해당 wrapper object를 구별한다.
typeof str; // string 타입
typeof strObj; // object 타입
wrapper object는 타입으로 사용해서는 안되며, primitive type
(number, string, boolean, symbol)과 참조 자료형
(Number, String, Boolean, Symbol)은 서로 다른 자료형이다.
let isDone: boolean = false;
isDone = true;
console.log(typeof isDone); // boolean
let isOk: boolean = true;
let isNotOk: boolean = new Boolean(true); // error, boolean 타입과 wrapper object Boolean은 다르기 때문이다.
let decimal: number = 6;
// 2진수 8진수 16진수도 사용 가능하다.
let hex: number = 0xf00d; // 16진수
let binary: number = 0b1010; // 2진수
let octal: number = 0o744; // 8진수
// NaN은 number 타입에 해당한다.
let notANumber: number = NaN;
// 천 단위로 _를 사용해 끊어 표기가 가능하다.
let underScore: number = 1_000_000;
let name: string = "hodu";
name = "angel";
let fullName: string = "hodu angel";
let age: number = 10;
let sentence: string = `Hello, i am ${name}, and i'm ${age} years old`;
debugging 시 참고할 description으로 사용되며, 특별한 의미를 가지진 않는다.
wrapper object나 메서드가 아닌 데이터이다.
// 타입스크립트에서 Symbol타입이 인식이 안될 땐 tsconfig.json파일에서 "lib" : [], 주석을 해제하고 ["ES2015", "DOM"]을 추가한다.
console.log(Symbol("foo")); // Symbol은 String이나 Number와 같은 래퍼 객체가 아니다.
console.log(Symbol("foo") === Symbol("foo")); //false
Symbol은 primitive type의 값을 담아서 사용한다.
해당 값을 고유하고 수정 불가능한 값으로 만들어준다.
접근을 제어하는데 많이 쓰인다.
const sym = Symbol();
const obj = {
[sym]: "value",
};
// obj.sym 또는 obj["sym"] 으로는 접근할 수 없다
console.log(obj[sym]); // "value"
undefined와 null은 각, undefined 및 null 이라는 타입을 가진다.
void와 마찬가지로 그 자체로는 유용하지 않다. 둘다 소문자만 존재한다.
let u: undefined = undefined;
let n: null = null;
따로 설정하지 않는 이상, undefined와 null은 모든 다른 타입의 서브 타입으로 존재한다.
number 타입 변수에 null이나 undefined를 할당 할 수 있다는 의미이다.
하지만 데이터의 정확도와 안정성을 높이기 위해서 이러한 상황을 방지하는 것이 좋다.
tsconfig.js파일에서 "strict": true,
또는 --strictNullChecks
주석을 해제하면 null은 자기 자신에게만, undefined는 자기 자신과 void에만 할당이 가능하다.
하지만 이렇게 strict한 상황에서 코딩하는 것은 매우 힘들다. 이 경우 union type
을 활용해 null과 undefined 할당을 가능하게 할 수 있다.
let name: number = undefined; // error
let u: undefined = null; // error
let v: void = undefined // void는 타입이고, 값으로는 존재할 수 없다. void에는 undefined 할당이 가능하다.
// union type으로 설정하여, null과 string을 모두 사용할 수 있게 한다.
let union: string | null = null;
union = "hodu";
TypeScript 에서 object는 "primitive type이 아닌 것"을 나타내고 싶을 때 사용하는 타입이다.
즉, 원시 자료형을 제외한 모든 자료형
을 의미한다.
const person1 = { name: "hodu", age: 10 };
// person1은 object 타입이 아니다.
console.log(typeof person1); // "{name: string, age: number}" type
const person2 = Object.create({ name: "Mark", age: 39 }); // 괄호 안에는 객체 형태 또는 null을 넣을 수 있다.
const person3 = Object.create(39); // error
const person4 = Object.create([]); // 배열도 가능하다.
const person5 = Object.create(function () {}); // 함수도 가능하다.
// 함수는 주로 아래와 같이 사용한다.
declare function create(o: object | null): void;
create({ prop: 0 }); //ok
create(null); //ok
create(42); // error
create("string"); // error
create(false); // error
create(undefined); // error
let list: number[] = [1, 2, 3];
// 리액트와 사용할 때 jsx언어와 충돌할 수 있어서 첫번째 표현식을 권장한다.
let list2: Array<number> = [1, 2, 3];
// union type 지정이 가능하다.
let list3: (number | string)[] = [1, 2, 3, "4"];
let x: [string, number];
x = ["hodu", 10];
x = [10, "hodu"]; // error
const person: [string, number] = ["hodu", 10];
const [one, two, three] = person;
// one은 string, two는 number
// 지정한 tuple의 길이를 초과했기 때문에 three는 error.
any 타입의 변수에는 어떤 타입의 값도 올 수 있다. 타입 시스템의 안정성을 위해 any 사용은 권장되지 않는다.
function returnAny(message: any): any {
// 이 함수의 리턴 값은 어떤 값도 될 수 있다.
console.log(message);
}
// any 타입은 전파된다.
function leakingAny(obj: any){
const a = obj.num;
const b = a + 1;
return b;
}
// function 안 변수들의 타입은 모두 any이다.
const c = leakingAny({ num: 0 });
function leakingAny(obj: any) {
const a: number = obj.num; // 이렇게 누수를 막으면 이 이후에 변수들의 타입은 number가 된다.
const b = a + 1;
return b;
}
모르는 변수의 타입을 묘사해야 할 때(ex. 동적 컨텐츠 값을 받아와야 할 때) 컴파일러와 미래의 코드를 읽는 사람에게 이 변수가 어떤 타입도 될 수 있음을 알려주는 unknown type을 활용한다.
이 때 런타임 과정에서 타입가드인 if문을 통해 unknown 타입을 한정시켜 사용한다.
declare const maybe: unknown;
const aNumber: number = maybe; // error
만일 maybe가 any였다면 여기서 error가 발생하지 않는다. 그렇게 되면 개발자가 예상치 못한 곳에서 실수할 가능성이 늘어난다.
unknown은 컴파일러가 타입을 추론할 수 있도록 타입의 유형을 좁히거나 타입을 확정해주지 않으면 다른 곳에 할당할 수 없고, 사용할 수 없다.
// 타입가드
if(maybe === true) {
const aBoolean: boolean = maybe; // maybe가 true 일 때만 작동하기 때문에 여기서 maybe는 boolean 타입으로 에러가 발생하지 않는다.
const aString: string = maybe; // error, maybe는 boolean 타입이기 때문에 string 타입과 맞지 않아 에러가 발생한다.
}
// maybe의 타입이 string 일 때만 작동하기 때문에 에러가 발생하지 않는다.
if (typeof maybe === "string") {
const aString: string = maybe;
}
any 대신 unknown을 활용하면 runtime error를 줄일 수 있다. (타입가드 덕분에)
never 타입은 모든 타입의 subType이며, 모든 타입에 할당할 수 있다. 하지만 never에는 그 어떤 것도 할당할 수 없다.
어떤 값도 올 수 있는 any조차도 never에 할당할 수 없다.
주로 잘못된 타입을 넣는 실수를 방지하고자 할 때 사용한다.
function error(message: string): never {
throw new Error(message);
//return 값이 없기 때문에 never를 지정할 수 있다.
}
function fail() {
// 리턴하고 있는 에러 함수에 리턴 타입이 never이기 때문에 이 함수의 return 타입도 never이다
return error("failed");
}
function infiniteLoop(): never {
while (true) {}
// 출구 없는 while 문으로 return 값이 없어 return type은 never 이다.
}
let a: string = "hello";
if (typeof a !== "string") {
a; // 여기서 a의 타입은 never이다.
}
declare const b: string | number;
if (typeof b !== "string") {
b; // 여기서 b의 타입은 number이다.
}
function returnVoid(message: string) {
// return이 없는 함수의 경우 기본 타입은 void
console.log(message);
}
const r = returnVoid("리턴이 없다."); // 여기서 r의 타입도 void가 된다.
const r: undefined = returnVoid("리턴이 없다."); // error
function returnVoid(message: string): void {
// void가 지정된 함수는 해당 함수의 리턴값을 사용하지 않겠다는 명시적 표현이다.
console.log(message);
return undefined; // 유일하게 undefined만 리턴할 수 있다.
}
https://medium.com/sjk5766/es-6-symbol-%EC%9D%B4%EB%9E%80-48c2ad5b054c