참고
TS 인터페이스
NOTE
타입스크립트의 인터페이스는 타입의 ‘틀’로서 사용할 수 있다.
•
•
목차
NOTE
•
•
1. 목차
NOTE
•
•
목차
NOTE
•
•
타입스크립트의 인터페이스는 타입의 ‘틀’로서 사용할 수 있다.
// 인터페이스명은 대문자로 짓는다
interface Human {
name: string; // name 키는 문자열 타입
age: number; // age 키는 넘버 타입
boo(): void; // boo 함수는 void 타입
}
// 인터페이스 자체를 타입으로 줘서 객체 생성
const person: Human = {
name: "da",
age: 5,
boo: () => console.log("this is boo"),
};
// 매개변수에서 인터페이스를 타입으로 받는다.
function booboo(a: Human): void {
console.log(`${a.name} is ${a.age} years old`);
};
booboo(person); // da is 5 years old
person.boo(); // this is boo
TypeScript
복사
선택적 프로퍼티 ( ?:)
선택적 프로퍼티를 사용하면 값이 없을 수 있다. 사실 Union타입으로 number | undefined 선언과 동일하다.
// 둘이 표현하고자 하는 타입 구조는 같다.
interface IUser {
name: string;
age?: number;
}
interface TUser = {
name: string;
age: number | undefiened;
}
TypeScript
복사
읽기 전용 프로퍼티(readOnly)
읽기 전용 속성은 단어 그대로, 인터페이스로 객체를 처음 생성할 때만 값을 할당하고 그 이후에는 변경할 수 없는 속성을 의미한다.
interface User{
name: string;
age: number
gender?: string
readonly birthYear: number
}
let user: User = {
name: 'jeff',
age: 30,
birthYear: 2010
}
TypeScript
복사
[readonly vs const]
readonly와 const 는 처음 초기화 할때만 값을 선언하고, 그후에는 값을 수정하지 못한다는 점에서 유사하다. 다만 이 둘은 사용처가 다른데, 변수는 const를 쓰고 프로퍼티는 readonly를 사용된다는 점만 기억하면 된다.
만약 모든 속성이 readonly일 경우, 일일히 프로퍼티마다 readonly를 찍어주지말고, 따로 유틸리티나 단언 타입을 활용하자.
interface IUser {
name: string;
age: number
}
let user2 : Readonly<IUser> = {
name: 'Neo',
age: 36
}
// user2.age = 85;
// user2.name = 'Evan'
TypeScript
복사
인터페이스 호환 / 확장
같은 이름의 인터페이스 여러 개를 만들 수 도 있다. 이들은 중복이 안되면, 안의 선언된 프로퍼티가 하나로 합쳐진다. 또한 extends를 사용하면 확장을 구현할 수 있다.
interface IFullName {
firstName: string,
lastName: string
}
interface IFullName {
middleName: string
}
// 호환(동일 인터페이스가 합쳐짐)
const fullName: IFullName = {
firstName: 'Tomas',
middleName: 'Sean',
lastName: 'Connery'
};
TypeScript
복사
interface Person {
name: string;
}
interface Developer extends Person {
skill: string;
}
// 확장
let fe: Developer = { name: 'josh', skill: 'TypeScript' };
TypeScript
복사
인터페이스 함수 타입
인터페이스는 함수의 모양을 정의할 수 있으며, 이를 전문 용어로 호출 시그니처라 부를 수 있다.
인터페이스로 함수 자체 타입을 정의할 때 다음과 같이 함수 속성 값도 정의할 수 있다.
interface GetText {
(name: string, age: number): string; // 함수
totalCall?: number // 변수?
}
const getText: GetText = (name, age) {
if(getText.totalCall !== undefined){
getText.totalCall += 1;
console.log(`totalCall: ${getText.totalCall}`)
}
return '';
}
getText.totalCall = 0;
getText('', 0);
getText('', 0);
TypeScript
복사
호출 시그니처는, 자바스크립트 객체의 prototype에 메소드를 추가적으로 등록할때도 유용히 사용된다.
다음은 자바스크립트의 기본 객체인 Object에 getshortKeys()라는 새로운 메서드를 프로토타입으로 등록한다.
interface Object {
getShortKeys(this: object): string[]
}
Object.prototype.getShortKeys = function () {
return Object.keys(this).filter((key) => key.length <= 3);
};
const obj = {
a: 1,
bb: 2,
ccc: 3,
dddd: 4
}
console.log(obj.getShortKeys())
TypeScript
복사
인덱스 타입
규칙이 있는 속성이라면, 변수라는 곳에 값을 넣어 유기적으로 이용하듯이, 인터페이스도 이를 활용할 수 있다.
type Score = 'A' | 'B' | 'C' | 'D' | 'F';
interface User {
name: string;
[grade: number]: Score; // indexable 타입 (선택적 프로퍼티 처럼 반드시 선언 안해줘도 된다.)
}
const user1: User = {
name: '홍길동',
1: 'A',
};
const user2: User = {
name: '임꺾정',
3: 'F',
};
const user3: User = {
name: '박혁거세',
2: 'B',
};
TypeScript
복사
interface IItem {
[itemIndex: number]: string // Index signature
}
let item: IItem = ['a', 'b', 'c']; // Indexable type
console.log(item[0]);
console.log(item[1]);
console.log(item['0']); // Error - itemIndex 인덱서블 속성은 number 타입이라 에러
TypeScript
복사
타입 추론
개발자가 굳이 변수 타입을 선언하지 않더라도 컴파일이 스스로 판단해서 타입을 넣어준다.
function add(x: number, y: number) { return x + y }; // 리턴 타입 명시 X
const result = add(1, 2); // 리턴 타입을 명시 안해줘도 어차피 number타입끼리의 연산은 number일테니 tsc가 이정도의 추론은 알아서 해준다.
TypeScript
복사
기본적인 타입에 대해서는 컴파일러가 잘 추론해주지만, 가끔 이상하게 하는 경우가 있다.
const obj = {
red: 'apple',
yellow: 'banana',
green: 'cucumber',
};
const obj = {
red: string,
yellow: string,
green: string,
};
TypeScript
복사
•
각 타입이 상수가 아닌 string으로 컴파일러는 인식한다.
타입 단언
타입 단언이란 이 타입은 특정 타입이라고 컴파일러에게 단언하는 것이다.
즉 컴파일러가 추론하지 않고, 직접 지시해서 타입을 줄 수 있다.
console.log(obj.getShortKeys())
let assertion: unknown = "타입 어셜선 단언"
let assertions_count: number = (assertion as string).length
TypeScript
복사
타입스크립트의 느낌표 연산자를 사용해서 non-null을 보장할수도 있다.
const div = document.querySelector("div")!
div!.innerHTML = "asdf"
TypeScript
복사
타입 가드
타입 가드는 에러를 줄일 수 있는 방어 코드 기법을 의미한다.
타입 가드는 어떠한 전용 문법이 있는 것이 아니라, 타입 스크립트에서 우리가 흔히 쓰는 if문 조차도 분기를 잘해서 오류를 최소화할 수 있다면 그것이 바로 타입 가드 기법이 될 수 있다.
대표적인 타입 가드에는 다음과 같은 방법이 있다.
1.
typeof: 일반 타입 체크
2.
Array.isArray() : 배열 체크
3.
.type / in : 객체 속성 체크
일반 타입 체크
function numOrStr(a: number | string){
if(typeof a === 'string'){
a.split(',')
}
if(typeof a === 'number'){
a.toFixed(1);
}
}
TypeScript
복사
function numOrNumArr(a: number | number[]){
if(Array.isArray(a)){
a.slice(1);
} else{
a.toFixed(1);
}
}
TypeScript
복사
type Lion = { gender: 'F' | 'M', bite: boolean }
type Ant = { gender: 'F' | 'M', acid: boolean }
type Sparrow = { gender: 'F' | 'M', fly: boolean }
function typeCheck(a: Lion | Ant | Sparrow){
if('bite' in a){
a.bite = true
} else if('acid' in a){
a.acid = true;
} else {
a.fly = true
}
}
TypeScript
복사
•
in 연산자 ⇒ 객체의 key값을 꺼내오는 기능이며, 특정 key값의 여부에 따라 동작하도록 설정한다.
null 타입가드
function sample(data: number[] | null) {
// 가장 먼저 data 가 null 인지 검사한다.
// JavaScript 에서 `typeof null == 'object'` 의 결과가 true 이기 때문에, null 여부를 판별할 수 없기 때문이다.
// 따라서 if 조건문에 data 가 유효한 값인지 판단하는 로직을 넣는다
if (data && typeof data === 'object') {
for (const number of data) {
console.log(number);
}
}
}
TypeScript
복사
•
for … of 문의 경우 반복가능한 객체의 요소를 반복하는데 사용된다.
제네릭
제네릭이란 타입을 변수화한것이다.
function add<T>(x: T, y: T): T {
return x + y;
}
add<number>(1, 2);
TypeScript
복사
// 제네릭 인터페이스
interface Mobile<T> {
name: string;
price: number;
option: T; // 제네릭 타입 - option 속성에는 다양한 데이터 자료가 들어온다고 가정
}
// 제네릭 자체에 리터럴 객테 타입도 할당 할 수 있다.
const m1: Mobile<{ color: string; coupon: boolean }> = {
name: 's21',
price: 1000,
option: { color: 'read', coupon: false }, // 제네릭 타입의 의해서 option 속성이 유연하게 타입이 할당됨
};
const m2: Mobile<string> = {
name: 's20',
price: 900,
option: 'good', // 제네릭 타입의 의해서 option 속성이 유연하게 타입이 할당됨
};
TypeScript
복사
type numOrStr = number | string;
// 제네릭에 적용될 타입에 number | string 만 허용
function identity<T extends numOrStr>(p1: T): T {
return p1;
}
identity(1);
identity('a');
// identity(true); //! ERROR
// identity([]); //! ERROR
// identity({}); //! ERROR
TypeScript
복사
객체 타입변환
typeof 연산자
객체 데이터 → 객체 타입 변환 연산자
const obj = {
red: 'apple',
yellow: 'banana',
green: 'cucumber',
};
// 위의 객체를 타입으로 변환하여 사용하고 싶을때
type Fruit = typeof obj;
/*
type Fruit = {
red: string;
yellow: string;
green: string;
}
*/
let obj2: Fruit = {
red: 'pepper',
yellow: 'orange',
green: 'pinnut',
};
TypeScript
복사
keyOf 연산자
객체 형태의 타입을 따로 속성만 뽑아서 유니온 타입으로 만들어준다.
type Type = {
name: string;
age: number;
married: boolean;
}
type Union = keyof Type;
// type Union = name | age | married
const a:Union = 'name';
const b:Union = 'age';
const c:Union = 'married';
TypeScript
복사
const obj = { red: 'apple', yellow: 'banana', green: 'cucumber' } as const;
type Key = typeof obj[keyof typeof obj]; // 객체의 value들만 가져와 상수 타입으로
let ob2: Key = 'apple';
let ob3: Key = 'banana';
let ob4: Key = 'cucumber';
TypeScript
복사
맵드 타입
mapped type이란 기존에 정의되어 있던 타입을 새로운 타입으로 변환해주는 문법을 의미한다.
•
인터페이스에 있는 모든 속성을 루프문 같이 순회해서 optionl(?)로 변경하거나 readonly로 지정한다.
interface Obj {
prop1: string;
prop2: string;
}
type ChangeType<T> = {
[K in keyof T]: number;
};
type Result = ChangeType<Obj>;
/*
{
prop1: number;
prop2: number;
}
*/
TypeScript
복사
타입스크립트 - 유틸리티 타입
Partial<T>
TYPE의 모든 속성을 선택적으로 변경한 새로운 타입을 반환한다.
interface User {
name: string;
age: number;
phone: number;
}
type Partial_User = Partial<User> // 인터페이스 User의 속성을 모두 optional 설정
/*
type Partial_User = {
name?: string | undefined;
age?: number | undefined;
phone?: number | undefined;
}
*/
const user: Partial<User> = {
name: 'B',
};
TypeScript
복사
Required<T>
TYPE의 모든 속성을 옵셔널에서 필수로 변경한 새로운 타입을 반환한다.
interface User {
name?: string;
age?: number;
phone: number;
}
type Required_User = Required<User>; // 인터페이스 User의 속성을 모두 일반 타입으로
/*
type Required_User = {
name: string;
age: number;
phone: number;
}
*/
const user: Required<User> = {
name: '홍길동',
age: 22,
phone: 111,
};
TypeScript
복사
ReadOnly<T>
Type의 모든 속성을 읽기전용으로 변경한다.
interface User {
name?: string;
age?: number;
phone: number;
}
type Readonly_User = Readonly<User>; // 인터페이스 User의 속성을 모두 readonly 설정
/*
type Required_User = {
readonly name?: string | undefined;
readonly age?: number | undefined;
readonly phone: number;
}
*/
const user: Readonly<User> = {
name: '홍길동',
age: 22,
phone: 111,
};
user.age = 11; // ERROR !!
TypeScript
복사
Record<Key, Type>
제네릭의 Key를 속성으로, 제네릭의 Type을 속성값의 타입으로 지정하는 새로운 타입을 반환합니다.
interface User {
name?: string;
age?: number;
phone: number;
}
type Readonly_User = Readonly<User>; // 인터페이스 User의 속성을 모두 readonly 설정
/*
type Required_User = {
readonly name?: string | undefined;
readonly age?: number | undefined;
readonly phone: number;
}
*/
const user: Readonly<User> = {
name: '홍길동',
age: 22,
phone: 111,
};
user.age = 11; // ERROR !!
TypeScript
복사
ReadOnly<T>
Type의 모든 속성을 읽기전용으로 변경한다.
interface User {
name?: string;
age?: number;
phone: number;
}
type Readonly_User = Readonly<User>; // 인터페이스 User의 속성을 모두 readonly 설정
/*
type Required_User = {
readonly name?: string | undefined;
readonly age?: number | undefined;
readonly phone: number;
}
*/
const user: Readonly<User> = {
name: '홍길동',
age: 22,
phone: 111,
};
user.age = 11; // ERROR !!
TypeScript
복사