Jieunny의 블로그
[TS] 1. Fundamentals 본문
✏️ 목차
📣 TS에서 타입 표현하기
✔️ ':'를 이용하여 코드에 타입을 정의하는 방식을 타입 표기(Type Annotation) 이라고 한다.
➰ 타입 표기는 필수가 아닌 선택 사항, 가능하면 적게! 사용해라.
➰ 대부분의 경우 타입 표기는 필요하지 않다.
// 타입 표기가 필요하지 않다. -> string 타입으로 추론된다.
let myName = 'Alice';
📍 원시타입
✔️ string, number, boolean
let str: string = "hi";
let num: number = 10;
let isLoggedIn: boolean = false;
📍 Array
let arr: number[] = [1, 2, 3];
let arr: Array<number> = [1, 2, 3]; //제네릭 사용
📍 Tuple
✔️ 배열의 길이가 고정되고, 각 요소의 타입이 지정되어 있는 배열 형식
let arr: [string, number] = ['hi', 10];
arr[1].concat('!'); //Error, 'number' does not have 'concat'
arr[5] = 'hello'; //Error, Property '5' does not exist on type '[string, number]'.
📍 Enum
✔️ 특정 값(상수)들의 집합
enum Avengers { Capt, IronMan, Thor }
let hero: Avengers = Avengers.Capt;
let hero: Avengers = Avengers[0];
// 인덱스를 사용자 편의로 변경해서 사용 가능
enum Avengers { Capt = 2, IronMan, Thor }
let hero: Avengers = Avengers[2]; // Capt
let hero: Avengers = Avengers[4]; // Thor
➰ 숫자형 이넘에서, 선언할 때 만약 이넘 값에 다른 이넘 타입의 값을 사용하면 선언하는 이넘의 첫 번째 값에 초기화를 해줘야 한다.
➰ 문자형 이넘은 이넘 값 전부 다 특정 문자 또는 다른 이넘 값으로 초기화 해줘야 한다.
📍Any
✔️ 모든 타입에 대해서 허용한다는 의미
let str: any = 'hi';
let num: any = 10;
let arr: any = ['a', 2, true];
let obj: any = { x: 0 };
// 아래 코드들은 오류 없이 실행된다.
// any를 사용하면 추가적인 타입 검사가 비활성화 된다.
obj.foo();
obj();
obj.bar = 100;
obj = 'hello';
const n: number = obj;
➰ 특정 값으로 인하여 타입 검사 오류가 발생하는 것을 원하지 않을 때 사용
➰ TS는 타입이 지정되니 않은 값에 대하여 문맥으로부터 타입을 추론해낼 수 없다면, 컴파일러는 any타입을 부여한다.
➰ 컴파일러 플래그 noImplicitAny : 암묵적으로 any로 간주하는 모든 경우에 오류를 발생시킨다.
📍 Void
✔️ 변수에는 undefined, null만 할당하고, 함수에는 반환 값을 설정할 수 없는 타입
let unuseful: void = undefined;
function notuse(): void {
console.log('sth');
}
📍 Never
✔️ 함수의 끝에 절대 도달하지 않는다는 의미를 지닌 타입(무한 루프)
function neverEnd(): never {
while(true) {
}
}
📍 함수
✔️ 함수의 매개변수 타입, 함수의 반환 타입, 함수의 구조 타입을 정의할 수 있다.
✔️ 함수의 매개변수 타입 표기
// 매개변수 타입 표기
function greet(name: string) {
console.log("Hello, " + name.toUpperCase() + "!!");
}
greet(42); // 해당 함수에 대한 인자 검사가 이루어지고, 맞지 않으면 런타임 오류 발생
greet('jieun');
greet('jieun', 'cinnamon'); // Error, too many parameters
➰ 함수의 매개변수를 설정하면 undefined나 null 이라도 인자로 무조건 넘겨야 한다.
➰ 정의된 매개변수 값만 받을 수 있고 추가로 인자를 받을 수 없다.
➰ '?'를 이용하면 매개변수의 갯수보다 인자를 적게 넘겨도 에러가 나지 않는다.
function sum(a: number, b?: number): number {
return a + b;
}
sum(10, 20); // 30
sum(10, 20, 30); // error, too many parameters
sum(10); // 타입 에러 없음
➰ Rest 문법이 적용된 매개변수
function sum(a: number, ...nums: number[]){
const totalOfNums = 0;
for (let key in nums) {
totalOfNums += nums[key];
}
return a + totalOfNums;
}
✔️ 함수의 반환 타입 표기
function getFavoriteNumber(): number {
return 26;
}
➰ 변수의 타입 표기와 마찬가지로, 반환 타입은 표기하지 않아도 되는 것이 일반적이다.
✔️ 익명 함수 -> 문맥적 타입 부여
➰ 함수가 코드상에서 위치한 곳을 보고 해당 함수가 어떻게 호출될 지 알아낼 수 있다면, TS는 해당 함수의 매개변수에 자동으로 타입을 부여한다.
const names = ['Alice', 'Bob', 'Eve'];
names.forEach(function (s) {
console.log(s.toUppercas());
// Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
});
➰ 매개변수 s에 타입이 표기되지 않았음에도 TS는 배열의 타입과 forEach 함수의 타입을 활용해서 s의 타입을 추론해냈다.
➰ 화살표 함수에도 적용된다.
✔️ this
➰ this가 가리키는 것을 명시할 때
interface Vue {
el: string;
count: number;
init(this: Vue): () => {};
}
let vm: Vue = {
el: '#app',
count: 10,
init: function(this: Vue) {
return () => {
return this.count;
}
}
}
let getCount = vm.init();
let count = getCount();
console.log(count); //10
📍 객체
✔️ 프로퍼티들과 각 프로퍼티의 타입을 나열
function printCoord(pt: { x: number; y: number }) {
console.log("The coordinate's x value is " + pt.x);
console.log("The coordinate's y value is " + pt.y);
}
printCoord({x: 3, y: 7});
➰ 매개변수 pt는 x, y 두 개의 프로퍼티로 이루어진 타입을 표기하고 있고, 두 값은 모두 number 타입이다.
➰ 각 프로퍼티를 구분할 때 ',' 또는 ';'를 사용할 수 있다.
➰ 일부 또는 모든 프로퍼티의 타입을 옵셔널(? 키워드)로 지정할 수 있다.
function printName(obj: { first: string; last?: string })
//...
}
printName({ first: 'Bob' }); //OK
printName({ first: 'Alice', last: 'Alisson' }); //OK
function printName(obj: { first: string; last?: string })
if(obj.last !== undefined) {
console.log(obj.last.toUpperCase());
}
// 이렇게 쓰거나
console.log(obj.last?.toUpperCase());
// 이렇게 써라
}
➰ 존재하지 않는 프로퍼티에 접근했을 때, 런타임 오류가 발생하지 않고 undefined 값을 얻게 된다.
➰ 옵셔널 프로퍼티를 읽었을 때, 해당 값을 사용하기에 앞서 undefined 인지 여부를 확인해야 한다.
📍 유니언 타입
✔️ 연산자를 사용하여 새로운 타입 만들기
function printId(id: number | string) {
// 문자열 또는 숫자를 받을 수 있는 함수
console.log("Your ID is: " + id);
}
printId(101);//OK
printId("202"); //OK
printId({ myId: 22342 });//error
function printId(id: number | string) {
// 문자열 또는 숫자를 받을 수 있는 함수
console.log(id.toUpperCase());
// string 타입에만 유효한 메서드는 사용할 수 없다.
}
function printId(id: number | string) {
// 문자열 또는 숫자를 받을 수 있는 함수
if(typeof id === 'string') {
console.log(id.toUpperCase());
}
else {
console.log(id);
}
}
➰ 코드상에서 유니언을 좁혀야 하는데, 조건을 나눠서 코드를 작성해준다.
➰ 유니언의 모든 멤버가 어떤 프로퍼티를 공통으로 가진다면, 좁히기 없이도 해당 프로퍼티를 사용할 수 있다.
📍 인터페이스
✔️ 인터페이스 선언은 객체 타입을 만드는 또 다른 방법
interface personAge {
age: number;
}
function logAge(obj: personAge) {
console.log(obj.age); //28
}
let person = { name: 'Capt', age: 28 };
logAge(person);
➰ TS는 printCoord에 전달된 값의 구조에만 관심을 가진다 -> TS가 구조적 타입 시스템이라고 불리는 이유
➰ 인터페이스에 정의된 속성, 타입의 조건만 만족한다면 객체의 속성 갯수가 더 많아도 된다.
➰ 인터페이스에 선언된 속성 순서를 지키지 않아도 된다.
✔️ 옵셔널 속성
interface CraftBeer {
name: string;
hop?: number;
}
let myBeer = {
name: 'Saporo'
};
function brewBeer(beer: CraftBeer) {
console.log(beer.name); // Saporo
console.log(beer.brewery); // Error: Property 'brewery' does not exist on type 'Beer'
}
brewBeer(myBeer);
➰ 인터페이스에 정의되지 있지 않은 속성에 대해서 오류를 표시한다.
✔️ 읽기 전용 속성
let myBeer: CraftBeer = {
brand: 'Belgian Monk'
};
myBeer.brand = 'Korean Carpenter'; // error!
➰ 읽기 전용 속성은 수정할 수 없다.
✔️ 읽기 전용 배열
let arr: ReadonlyArray<number> = [1,2,3];
arr.splice(0,1); // error
arr.push(4); // error
arr[0] = 100; // error
➰ 선언하는 시점에만 값을 정의할 수 있다.
✔️ 함수 타입
interface login {
(username: string, password: string): boolean;
}
let loginUser: login;
loginUser = function(id: string, pw: string) {
console.log('로그인 했습니다');
return true;
}
➰ 함수의 인자의 타입과 반환 값의 타입을 정한다.
✔️ 클래스 타입
interface CraftBeer {
beerName: string;
nameBeer(beer: string): void;
}
class myBeer implements CraftBeer {
beerName: string = 'Baby Guinness';
nameBeer(b: string) {
this.beerName = b;
}
constructor() {}
}
✔️ 인터페이스 확장
// 확장
interface Person {
name: string;
}
interface Developer extends Person {
skill: string;
}
let fe = {} as Developer;
fe.name = 'josh';
fe.skill = 'TypeScript';
// 상속
interface Person {
name: string;
}
interface Drinker {
drink: string;
}
interface Developer extends Person {
skill: string;
}
let fe = {} as Developer;
fe.name = 'josh';
fe.skill = 'TypeScript';
fe.drink = 'Beer';
✔️ 하이브리드 타입
➰ 함수 타입이면서 객체 타입을 정의할 수 있다.
interface CraftBeer {
(beer: string): string;
brand: string;
brew(): void;
}
function myBeer(): CraftBeer {
let my = (function(beer: string) {}) as CraftBeer;
my.brand = 'Beer Kitchen';
my.brew = function() {};
return my;
}
let brewedBeer = myBeer();
brewedBeer('My First Beer');
brewedBeer.brand = 'Pangyo Craft';
brewedBeer.brew();
📍 제네릭
✔️ 재사용성이 높은 컴포넌트를 만들 때 자주 활용된다.
✔️ 타입을 마치 함수의 파라미터처럼 사용하는 것
// 제네릭 기본 문법
function getText<T>(text: T): T {
return text;
}
// 함수 호출 시 아래와 같이 함수에서 사용할 타입을 넘겨줄 수 있다.
getText<string>('hi');
getText<number>(10);
getText<boolean>(true);
➰ getText<string>('hi') 를 호출하면 T는 string이 되고, text는 string타입이 되서 string 타입인 hi를 반환한다.
✔️ 제네릭을 사용하는 이유
function logText(text: any): any {
return text;
}
➰ 여러가지 타입을 허용하고 싶다면 any를 쓰면 되지만, any는 타입 검사를 하지 않기 때문에 함수의 인자로 어떤 타입이 들어갔고 어떤 타입이 반환되는지 알 수가 없다.
function logText<T>(text: T): T {
return text;
}
// #1
const text = logText<string>("Hello Generic");
// #2
const text = logText("Hello Generic");
➰ 제네릭으르 쓰면 함수를 호출할 때 넘긴 타입에 대해 TS가 추정할 수 있다.
✔️ 제네릭 타입 변수
➰ 제네릭에 타입을 줌으로써 유연한 방식으로 함수의 타입을 정의해줄 수 있다.
// 제네릭에 어떤 타입이 들어올 지 모르니까 length를 허용하지 않는다.
function logText<T>(text: T): T {
console.log(text.length); // Error: T doesn't have .length
return text;
}
function logText<T>(text: T[]): T[] {
console.log(text.length); // 제네릭 타입이 배열이기 때문에 `length`를 허용합니다.
return text;
}
function logText<T>(text: Array<T>): Array<T> {
console.log(text.length);
return text;
}
📍 타입 추론
✔️ 타입스크립트가 코드를 해석해 나가는 동작
✔️ 타입 추론의 기본
let x = 3;
➰ x에 대한 타입을 따로 지정하지 않더라도, 일단 x는 number로 간주된다.
➰ 변수를 선언하거나 초기화 할 때 타입이 추론된다.
➰ 외에도 변수, 속성, 인자의 기본 값, 함수의 반환 값 등을 설정할 때 타입 추론이 일어난다.
✔️ 가장 적절한 타입(Best Common Type)
➰ 타입은 보통 몇 개의 코드를 바탕으로 추론하며, 그 코드를 이용하여 가장 근접한 타입을 추론하게 되는 것을 가장 근접한 타입 이라고 한다.
let arr = [0, 1, null];
➰ arr의 타입을 추론하기 위해서 배열의 각 아이템을 살펴본다.
➰ 배열의 아이템은 크게 number와 null로 구분되며, 이 때 Best Common Type알고리즘으로 다른 타입들과 가장 잘 호환되는 타입을 선정한다.
✔️ 문맥상의 타이핑(Contextual Typing)
➰ 타입스크립트에서 타입을 추론하는 또 하나의 방법
➰ 문맥상으로 타입을 결정하는 것 -> 코드의 위치(문맥)을 기준으로 일어난다.
window.onmousedown = function(mouseEvent) {
console.log(mouseEvent.button); //<- OK
console.log(mouseEvent.kangaroo); //<- Error!
};
➰ window.onmousedown에 할당되는 함수의 타입을 추론하기 위해서 window.onmousedown 타입을 검사한다.
➰ 타입 검사가 끝나고 함수의 타입이 마우스 이벤트와 연관이 있다고 추론하기 때문에 mouseEvent 인자에 button 속성은 있지만 kangaroo속성은 없다고 결론 내린다.
✔️ 타입스크립트의 타입 체킹
➰ Duck Typing : 객체의 변수 및 메서드의 집합이 객체의 타입을 결정하는 것을 의미한다.
➰ Structural Subtyping : 객체의 실제 구조나 정의에 따라 타입을 결정하는 것을 의미한다.
📍 타입 호환
✔️ 특정 타입이 다른 타입에 잘 맞는지를 의미한다.
interface Ironman {
name: string;
}
class Avengers {
name: string;
}
let i: Ironman;
i = new Avengers(); // OK, because of structural typing
➰ Avengers 클래스가 Ironman 인터페이스를 상속받지 않았는데도 에러가 나지 않는다 -> 구조적 타이핑 때문
interface Avengers {
name: string;
}
let hero: Avengers;
// 타입스크립트가 추론한 y의 타입은 { name: string; location: string; } 입니다.
let capt = { name: "Captain", location: "Pangyo" };
hero = capt;
➰ capt가 hero 타입에 호환될 수 있는 이유는, capt의 속성 중에 name이 있기 때문이다.
function assemble(a: Avengers) {
console.log("어벤져스 모여라", a.name);
}
// 위에서 정의한 capt 변수. 타입은 { name: string; location: string; }
assemble(capt);
➰ capt 변수에 이미 name 속서어 뿐만 아니라 location 속성도 있기 때문에 assemble 함수의 호출 인자로 넘길 수 있다.
✔️ Soundness란?
➰ 타입스크립트는 컴파일 시점에 타입을 추론할 수 없는 특정 타입에 대해서 일단 '안전하다' 고 본다.
➰ 이걸 '들리지 않는다' 라고 표현한다.
📍 타입 별칭
✔️ 똑같은 타입을 재사용하거나, 또 다른 이름으로 부르고 싶은 경우
✔️ 별도로 이름 붙인 타입을 새로 작성하는 것
✔️ 인터페이스와 비슷하지만, 인터페이스와 다르게 확장이 불가능하다.
➰ 객체 타입
type Point = {
x: number;
y: number;
};
function printCoord(pt: Point) {
console.log('The coordinate's x value is " + pt.x);
console.log('The coordinate's y value is " + pt.y);
}
printCoord({ x: 100, y: 100 });
➰ 유니언 타입
type ID = number | string;
'Study > TypeScript' 카테고리의 다른 글
[TS] 함수와 메서드 (0) | 2023.02.01 |
---|---|
[TS] 객체와 타입 (2) | 2023.02.01 |
[TS] 타입스크립트 프로젝트 만들기 (0) | 2023.01.30 |
[TS] 2. Usage (0) | 2023.01.27 |
[TS] 0. TypeScript란? (0) | 2023.01.26 |