
잠깐의 회고
DOSOPT에서 활동하면서 처음으로 큰 책임감을 느꼈던 타입스크립트 스터디,,,, YB에서 OB로 전환되면서 정말 많은 것을 깨우칠 수 있었고, 그와 동시에 저번 기수에는 열리지 않았던 타입스크립트 스터디를 열게 되었다. 처음 YB로 들어왔을 때 타입스크립트를 바라보는 나의 막막한 시선(?)과 두려움을 일찍 깨주고 싶기도 했고 나도 다시 처음으로 돌아가서 공부를 해보고 싶기도 했다. 그렇게 열게 되었고 정말 고맙게도 6명이나! 나와 함께 해주었다!!
진짜 너무너무 고마워서 나름 열심히 끌어간다고 하긴 했는데, 과연 도움이 됐을지는 모르겠다(제발 도움이 됐길,,,ㅎㅎ) 이번에 타스 스터디를 진행하면서 스터디원을 정말 많이 성장시켜 주고 싶어서 나름 없는 시간 쪼개서 보충 세션도 준비하고, 세션 진행 싱황도 계속 확인해보았다!!
심화까지 가지 못한 부분이 많이 아쉬움이 남지만, 기본은 다 타파? 했다고 생각하고 스터디원들의 성장에 1%정도 기여했으면 그걸로 됐다!!
인터페이스는 객체 타입을 정의할 때 사용하는 문법이다.
인터페이스로 타입은 다음과 같이 지정할 수 있다.
1. 객체의 속성과 속성 타입
2. 함수의 파라미터와 반환 타입
3. 함수의 스펙(파라미터 개수와 반환값여부)
4. 배열과 객체를 접근하는 방식
5. 클래스
인터페이스로 정의된 객체의 속성을 일부만 사용하고 싶을 때, 옵션 속성(선택적 프로퍼티)을 사용한다.
프로퍼티의 이름 끝에?를 추가하면, 해당 속성이 있지 않더라도 에러가 나지 않는다.interface Person { name?: string; age: number } function logAge(someone: Person) { console.log(someone.age); } const person = { age: 100 }; logAge(person);
일부 프로퍼티들을 객체가 처음 생성될 때에만 수정 가능해야 하고,
프로퍼티 앞에 readonly 를 넣어서 지정한다.interface MynameIs { readonly firstName: string; //수정 불가능 lastName: string; } let iAm: string = { fristName: 'hyein', lastNmae: 'kwon'} ; iAm.firstName = 'minseo'; //error iAm.lastName = 'kang' //오류가 나지 않습니다! -> readonly가 아니라서
인터페이스를 상속받을 때에는
extends키워드를 사용한다.
이때 주의해햐 할 점은 상위 인터페이스의 타입을 하위 인터페이스에서 상속받아 타입을 정의할 때, 상위 인터페이스 타입과 호환이 되어야 한다는 점이다.
- 호환이 된다? : 상위 클래스에서 정의된 타입을 사용해야 함
interface Shoes { type: string; size: number; } interface Brand { brand: string; } interface Boots { type: string; ... } = 비효율! interface Boots extends Shoes, Brand { color: string; } let myShoes = { } as Boots; myShoes.type = 'boots'; myShoes.size=225; myShoes.brand='dont care'; myShoes.color = 'black';
인덱싱이란?
객체의 특정 속성을 접근하거나, 배열의 인덱스로 특정 요소에 접근하는 방식
a[10] ageMap["hyein"]
string[] <Array>특정 인덱스를 사용하여 값을 가져오는 타입을 지정하는 방법
interface TsGirls { [name: string ] : string; } interface Color { color: string } interface PropsType exteds TsGirls, Color { num: number } function 함수이름(props: PropsType) let tsGirlsList:TsGirls = ['hyein' ,'hoeun' , 'subin'] tsGirlsList[0]; // 'hyein'
객체의 프로퍼티에 동적으로 접근하고 값을 가져오거나 설정하는 타입
객체 인덱싱 타입을 정의하려면 중괄호 내부에 문자열 또는 숫자 리터럴 타입을 사용하여 프로퍼티 이름과 해당 프로퍼티의 값 타입을 지정interface MyDictionary { [ level : string ]: number }; let item: MyDictionary = { hehe : 100 }
여러 개의 타입 중 한 개만 쓰고 싶을 때 사용하는 문법
유니언 타입에서는 함수에 인자를 넘겨 실행할 때 어떤 타입이 올 지 알 수 없기 때문에, 어느 타입이 오더라도 문제없는 공통 속성만을 자동완성해준다.
동일한 이름의 속성을 정의하고, 타입을 다르게 주면서 구분하는 것이다.
interface Pencil { name : pencil; brand : string; write() : void; } interface Eraser { name : eraser; brand: string; erase() : void; } function getGift(gift: Pencil | Eraser) { console.log(gift.brand); if(gift.name === "pencil") { gift.write(); } else { gift.erase(); } }해당 예시와 같이
in연산자를 사용하여, 함수의 파라미터 타입의 종류에 따라 특정 로직을 수행할 수 있다.또한 마찬가지로
typeof연산자도 사용할 수 있다.function getGift(gift: string | number) { if(typeof gift === "string") { console.log(gift.toUpperCase()); } if (typeof gift === 'number') { console.log(gift.toLocaleString()); } }
타입 2개를 하나로 합쳐서 사용할 수 있는 타입이며 기존 타입을 대체하지 않으면서 기존 타입에 새로운 필드를 추가하고 싶을 때 사용한다.
interface Pencil { name : string; write() : void; } interface Eraser { name : string; color: string; price: number; } const eraserPencil: Eraser & Pencil = { name : "멋쟁이 지우개달린 연필", write(){}, color: "black", price: 99999999, }
특정 타입이나 인터페이스 등을 참조할 수 있는 타입 변수를 의미
타입에 의미를 부여해서 별도의 이름으로 부르는 것
객체 형태는 물론 computed value를 사용할 수 있다. (그냥 모든 유형 다 가능)
⇒ 기존 타입의 새 이름을 정의하는 방법, 타입 별칭(type alias)를 지정하는데 사용 가능
⇒ 타입 확장: & 사용
⇒ 유니온, 인터섹션에 유리
⇒ 함수 매개 변수 등 반환 타입을 포함하여 여러군데 쓰임type Union = string | number; type MyType = { isMale: boolean, age: number } type Anhyoseop = MyType & { name: '안효섭' | string } type Drama = { name: string; } type Drama = { isNetfix : boolean } //error: 식별자 중복 type IdealType = ( Mytype | Union ) & { isTall : boolean } //가능 하지만 interface에서는 표현할 수 없는 형식이다.
객체 형태(key-value)에 이름을 부여하는 것만 가능
⇒ 완전히 새로운 유형을 정의
⇒ 재 선언시 기존의 interface 객체에 더하여 확장
⇒ interface는 오직 객체 유형을 만드는데에만 사용interface Union { string | number //error } interface MyType { isMale: boolean, age: number } interface Anhyoseop extends MyType { name: '안효섭' | string } interface Drama { name: string; } interface Drama { isNetfix : boolean } //확장됨 : 선언 병합이라고 한다. const hyoseop: Drama = { name: '너의 시간 속으로', isNetflix : true }
🍀 interface 🍀
1. 객체 유형을 확장(선언병합)하거나 구현해야 하는경우
2. 함수의 매개 변수 및 반환 유형의 타입 지정하는 경우 (props 등입니다)
3. api를 설명하는 경우(api가 변경될 때 사용자가 인터페이스를 통해 새로운 필드를 병합할 수 있기 때문)
🍀 type 🍀
1. 유니온, 인터섹션 타입 및 조건부 타입과 같은 사용자 정의 유형
2. 별칭을 사용하여 코드를 더 쉽게 이해하게 하고 싶을 때
3. 제네릭, 유틸리티 타입, 맵드 타입과 연동하고 싶을 때
타입을 미리 정의하지 않고 사용하는 시점에 원하는 타입을 정의해서 쓸 수 있는 문법
function printInfo(info) { console.log(info); return info; } printInfo(10); // 숫자 10 printInfo('하이'); // 문자열 하이 printInfo(true); // 진위값 true //제네릭활용 function printInfo<T>(info: T): T { console.log(info); return info; } printInfo<number>(10); printInfo<string>('하이'); printInfo<boolean>(true);
인터페이스를 정의할 때, 각 다른 데이터 타입을 가지게 된다면, 계속해서 인터페이스를 정의해야하는 문제가 생긴다.
interface ProductDropDown { value : string; selected: boolean; } interface ProductDropDown { value : number; selected: boolean; }위와 같은 코드를 제네릭으로 바꾸면 다음과 같다.
interface Dropdown<T>{ value: T; selected: boolean; }
동일한 로직임에도 불구하고 타입이 달라서 각각 타입에 따른 함수를 선언해주어야 한다.
function printStrInfo(info: string) { console.log(info); return info; } function printNumInfo(info: number) { console.log(info); return info; } printStrInfo('a'); printNumInfo(10);위와 같은 문제를
유니언으로 해결한다면,
함수 내부에서 로직을 작성할 때 문제가 생길 수 있다.
- 공통으로 접근할 수 있는 api나 속성에 대해서만 접근 가능하며, 반환값은 여전히 유니온 타입을 가져 이후 속성에 접근하려면 에러가 발생한다.
따라서 이를 제네릭으로 바꾸면
function printInfo<T>(info: T): T { console.log(info); return info; } const str = printInfo<string>('hi'); str.split(''); // 접근 가능 ! const login = printInfo<boolean>(true);해결할 수 있다.
extends 키워드를 사용한 타입 제약interface LengthType {
length: number;
}
function printInfoLength<T extends LengthType>(info: T): T {
console.log(info.length); // 에러 발생하지 않음!
return info;
}
printInfoLength('hello 나는 문자열'); // length가 메소드로 적용됨
printInfoLength({ length: 10 }); //length를 가진 객체도 전달 가능!
printInfoLength({ len: 10 }); //에러 발생 len이라는 값이 없기 때문 !
printInfoLength(10); // 에러 발생 number에는 length 내장함수가 없기 때문 !!
또는,
function printInfoLength<T extends string>(info: T): T {
console.log(info.length); // 에러 발생하지 않음!
return info;
}
다음과 같이 제약하여, 원하는 타입만 제네릭에서 처리하도록 만들어줄 수 있다.
keyof를 사용한 타입 제약interface FoodCart {
name: string;
price: number;
quantity: number;
}
function getFoodCartOption<T extends keyof FoodCart>(foodOption: T): T {
return foodOption;
}
getFoodCartOption<string>('a'); // 에러 발생
getFoodCartOption('name');
keyof를 사용해서, 객체의 키 값만 타입으로 받겠다고 정의할 수 있다.