728x90
SMALL
※ 인프런 - 타입스크립트 시작하기(by 이재승) 강의를 기반으로 정리한 내용입니다.
타입 추론(Type Inference)
정적 타입 언의 단점은 타입을 정의하는데 많은 시간과 노력이 들기 때문에 생산성이 저하될 수 있다는 점이다. 하지만 타입스크립트의 경우 다양한 경우에 대해 타입 추론을 제공하기 때문에 꼭 필요한 경우에만 타입 정의를 할 수 있다. 여기서 말하는 타입 추론이란, 타입을 프로그래머가 따로 정의하지 않아도 자동으로 타입을 추론해주는 기능을 말한다. 타입 추론 덕분에 코드를 덜 작성하면서도 같은 수준의 타입 안정성을 유지할 수 있다는 점이 타입스크립트의 장점!
export {};
let v1 = 123;
let v2 = "abc";
v1 = 'a'; // number 형식에 string 할당 불가능
v2 = 456; // string 형식에 number 할당 불가능
/**
* v1, v2 변수에 타입을 정의하지 않았음에도 불구하고 v1은 숫자, v2는 문자열로 타입이 정의됨
* 즉 자동으로 타입을 추론해 줌
*/
const v4 = 123; // v4의 타입은 number가 되는 것이 아니고 123타입을 가지게 됨
const v5 = 'abc';
let v6: typeof v1 = 234;
let v7: typeof v4 = 234; // v7은 123타입을 가지기 때문에 에러 발생
/**
* let은 재할당이 가능한 반면 const는 재할당이 불가능하기 때문에 let변수보다 엄격하게 타입이 결정됨 (물론 let도 재선언은 불가)
* v7같은 경우도 123 타입만 사용 가능
*/
// 배열과 객체의 타입 추론
const arr1 = [10, 20, 30]; // 배열의 타입을 정의하지 않아도 숫자 원소들만 있으면 타입은 number로 정의됨
const [n1, n2, n3] = arr1; // 비구조화(destructing) 할당을 하는 경우에도 각 변수는 자동으로 타입 추론이 됨
arr1.push('a'); // string 형식이기 때문에 에러
const obj = { id: 'asdf', age: 123, langauage: 'ko' }; // 객체 역시 자동으로 타입 추론이 됨
const { id, age, langauage } = obj; // 마찬가지로 비구조화 할당을 했을 때도 자동으로 타입 추론 됨
console.log(id === age); // error TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap
// 인터페이스에서의 타입 추론
interface Person {
name: string;
age: number;
}
interface Korean extends Person {
liveInSeoul: boolean;
}
interface Japanese extends Person {
liveInTokyo: boolean;
}
const p1: Person = {name: 'mike', age: 23};
const p2: Korean = {name: 'mike', age: 25, liveInSeoul: true};
const p3: Japanese = {name: 'mike', age: 27, liveInTokyo: false};
const arr2 = [p1, p2, p3]; // const arr2: Person[]
const arr3 = [p2, p3]; // const arr3: (Korean | Japanese)[]
/**
* 배열의 각 원소들의 타입이 다를 때는? 여러 가지 타입을 하나로 통합하는 과정을 거침
* 다른 타입으로 할당 가능한 타입은 제거됨
* arr1의 경우 Korean과 Japanese 타입은 둘 다 Person에 할당 가능하기 때문에 제거되고 Person만 남음
* arr2은 서로 할당관계에 있지 않기 때문에 제거되는 것은 없고 유니온 타입으로 묶여서 나옴
*/
// 함수의 타입 추론
function func1(a= 'abc', b = 10) {
return `${a}, ${b}`;
}
func1(3, 6); // 첫 번째 매개변수는 문자열 타입이기 때문에 에러
const v8: number = func1('a', 1); // 함수는 문자열 타입을 반환하기 때문에 에러
/**
* 매개변수에 타입을 정의하지 않아도 값의 타입에 따라 자동으로 타입 선언됨
* 즉 a는 string, b는 number 타입으로 정의됨
* 또한 함수가 문자열을 반환하기 때문에 함수의 타입은 string으로 자동 정의됨
*/
function func2(value: number) {
if (value < 10) return value;
else return `${value} is too big`;
}
/**
* 반환이 여러 개 있는 함수도 타입 추론 가능
* func2의 타입은 number | string 으로 정의됨
* 이렇게 반환이 많은 경우 타입을 따로 입력하지 않아도 자동으로 타입이 추론되기 때문에 편하게 코드 작성 가능
* 물론 필요한 경우 명시적으로 입력하면 됨!
*/
타입 가드(Type Guard)
타입 가드는 자동으로 타입의 범위를 좁혀 주는 타입스크립트의 기능이다. 타입가드를 잘 활용하면 "as" 와 같은 타입 단언 코드를 피할 수 있기 때문에 가독성이 좋아진다.
export {};
function print(value: number | string | object) {
if (typeof value === 'number' || typeof value === 'object') console.log((value as number).toFixed(2));
else console.log((value as string).trim());
}
/**
* as? as 앞에 있는 값의 타입을 개발자가 확신하는 경우에 그 타입을 적어줌으로써 타입스크립트에게 오른쪽에 있는 타입을 강제하는 기능
* 타입스크립트는 number라고 생각하고 있지 않지만 개발자는 number라고 타입스크립트한테 주입하는 것
* 이 키워드는 정말 어쩔 수 없는 경우에만 사용. as를 많이 사용할수록 버그의 위험도 높아짐
* 위의 함수를 예로 들면, value의 타입이 number이거나 object일 수 있는데 as를 통해 number로 강제함 --> 버그 위험
*/
function print2(value: number | string) {
if (typeof value === 'number') console.log((value).toFixed(2));
else console.log((value).trim());
}
/**
* 위 함수에서의 typeof는 자바스크립트의 typeof로 타입스크립트의 그것과는 다름
* 위 함수의 typeof는 오른쪽 변수의 타입을 문자열로 반환하는 기능
* 타입가드 기능 덕분에 as를 사용하지 않아도 if문의 value가 각각 number, string인 것을 인식하기 때문에 각각의 메소드 사용 가능
*/
// class 타입가드
/**
* 클래스의 경우 instanceof라는 키워드 사용 가능
* instanceof는 자바스크립트에도 있는 기능(값의 영역에서 사용하는 기능)
* 아래 함수 print3의 instanceof를 예시로 보면, value가 Person 클래스에 포함된 속성인지 검사함
*/
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
class Product {
name: string;
price: number;
constructor(name: string, price: number) {
this.name = name;
this.price = price;
}
}
function print3(value: Person | Product) {
console.log(value.name);
if (value instanceof Person) console.log(value.age);
else console.log(value.price);
}
/**
* 타입스크립트는 if문에서 value가 Person의 객체라는 것을 알고 else에 있는 value가 Product의 객체라는 것을 앎
* 그래서 타입가드가 적용되어서 value.age, value.price 사용 가능
*/
interface Person1 {
name: string;
age: number;
}
interface Product1 {
name: string;
price: number;
}
function print4(value: Person1 | Product1) {
console.log(value.name);
if (value instanceof Person1) console.log(value.age); // Person1은 형식만 참조, 값으로 사용되지 않기 때문에 에러!
else console.log(value.price);
}
/**
* 그러나 위의 경우처럼 인터페이스로 타입을 정의하면 에러가 발생함
* 인터페이스는 타입을 위한 코드이기 때문에 컴파일 후에는 다 제거되기 때문
* 그런데 instanceof는 값의 영역에 있기 때문에 실제 컴파일 후에 돌아가는 코드임
* Person1은 컴파일 후에 존재하지 않기 때문에 에러 발생. instanceof 오른쪽에는 클래스나 생성자함수가 올 수 있음
*/
// discriminated union
interface Person {
type: 'a';
name: string;
age: number;
}
interface Product {
type: 'b';
name: string;
price: number;
}
function print5(value: Person | Product) {
if (value.type === 'a') console.log(value.age);
else console.log(value.price);
}
/**
* 인터페이스를 구별하기 위한 방법: 식별 가능한 유니온 타입을 이용하는 것(영어로 discriminated union이라 불림)
* 인터페이스에서 식별 가능한 유니온 타입은 같은 이름의 속성을 정의하고 속성의 타입은 모두 겹치지 않게 정의하면 됨
* 위의 print5 함수를 예로 들면, Person과 Product에는 type이라는 같은 이름의 속성을 정의하고, 각각 a와 b라는 타입으로 겹치지 않게 정의함
* print5에서 value.type이 'a'이면 Person타입, 아니면 Product 타입
* as와 같은 타입 단언 코드를 작성하지 않았음에도 Person에만 있는 age라는 속성을 사용하려고 할 때 에러가 나지 않음
* 왜? 타입가드가 동작하기 때문
*/
// 식별 가능한 유니온 타입은 서로 겹치지 않기 때문에 switch문에서 사용하기 좋음
function print6(value: Person | Product) {
switch (value.type) {
case 'a':
console.log(value.age);
break;
case 'b':
console.log(value.price);
break;
}
}
/**
* switch문의 조건으로 타입 속성값을 입력하면 각각의 case에서 타입을 구분해서 처리할 수 있음
*/
// 타입 가드를 활용하는 또다른 방법: 타입을 검사하는 함수를 작성하는 방법
interface Person {
name: string;
age: number;
}
interface Product {
name: string;
price: number;
}
function isPerson(x: Person | Product): x is Person {
return (x as Person).age !== undefined;
}
function print7(value: Person | Product) {
if (isPerson(value)) console.log(value.age);
else console.log(value.price);
}
/**
* 위의 인터페이스에는 type 속성이 없고, age와 price 속성의 차이로 구별할 수 있음
*/
//위의 방법보다 속성 이름을 검사하는 방법이 더 간편함
function print8(value: Person | Product) {
if ('age' in value) console.log(value.age);
else console.log(value.price);
}
/**
* 하지만 속성의 수가 많고, 같은 이름이 중복되면 이런 방법은 사용하기 힘들기 때문에 그럴 때는 식별 가능한 유니온 타입을 사용하면 더 좋음
*/
728x90
LIST
'개발자 도전기 > [STUDY] JS || TS' 카테고리의 다른 글
TypeScript | 강의 메모 | CLI에서 동작하는 todo앱 프로젝트 - Todo 추가, 삭제하기(1) (0) | 2022.04.14 |
---|---|
TypeScript | 강의 메모 | CLI에서 동작하는 todo앱 프로젝트 (0) | 2022.04.13 |
TypeScript | 강의 메모 | 제네릭(Generic), 맵드 타입(Mapped Type), 조건부 타입(Conditional Type) (0) | 2022.04.01 |
TypeScript | 강의 메모 | 타입 호환성 (0) | 2022.04.01 |
TypeScript | 강의 메모 | 타입 정의 (0) | 2022.03.31 |
댓글