10. 호출 시그니처, 인덱스 시그니처
호출 시그니처(call signature)
interface getLikeNumber { // 계속 사용하고싶다면 interface생성
(like: number): number; // call signature
}
interface Post {
id: number;
title: string;
getLikeNumber: getLikeNumber;
}
const post1: Post = {
id: 1,
title: "post 1",
getLikeNumber(like: number) {
return like
}
}
post1.getLikeNumber(1);
인덱스 시그니처(index signature)
속성의 모든 이름을 미리 알지 못하는 경우가 있지만 값의 형태는 알고 있을때 index signature를 사용하여 가능한 값의 Type을 지정할 수 있다.
객체 인덱스 시그니처
계속 속성이 더해져서 post1객체에 모든 속성의 이름을 알지 못할때에는 인덱스 시그니처를 사용할 수 있다.
// - 객체 인덱스 시그니처
interface Post {
[key: string]: unknown; // key 부분은 다른이름으로 사용가능 props나..
id: number;
title: string;
}
const post1: Post = {
id: 1,
title: "post 1",
}
post1['description'] = 'description1';
post1['pages'] = 300;
배열 인덱스 시그니처
// - 배열 인덱스 시그니처
interface Names {
[item: number]: string;
}
const userNames: Names= ['John', 'Kim', 'Joe'];
11. 함수 오버로딩
👉두 함수를 하나로 만들어주려면
기본적인 구조는 같지만 매개변수가 다를때 오버로딩을 이용해서 두 함수를 하나로 만들어 줄 수 있다.
// 1. overloading1
function add(a: string, b: string): string;
function add(a: number, b: number): number;
function add(a: any, b: any): any {
return a + b;
}
add(1,2); // number
add("hello", "world"); // string
// add(1, "world"); // error
// 2. overloading2
function saySomething(word: string): string;
function saySomething(words: string[]): string;
function saySomething(word: any): string {
if (typeof word === "string") {
return word;
} else if (Array.isArray(word)) {
return word.join(" "); // string
}
throw new Error("unable to say something");
// string으로 return하는데 undefined될 수 있어서 error throw함
}
saySomething('hello');
saySomething(['hello', 'world']);
12. 접근 제어자
생성자를 이용해서 매개변수 2개를 가져오는데,
가져오는 매개변수를 this.id와 this.title에 할당하는데
이 둘다 타입에러가 나오고 있다.
// type error
class Post {
constructor(id: number, title: string) {
this.id = id;
this.title = title;
}
getPost() {
return `postId: ${this.id}, postTitle: ${this.title}.`;
}
}
let post: Post = new Post(1, "title 1");
타입스크립트에서는 this로 접근하는 속성들을 위한 타입이 class의 body에 지정되어 있어야한다.
class Post2 {
id: number; // 속성 type 지정. public default
title: string;
constructor(id: number, title: string) {
this.id = id;
this.title = title;
}
getPost() {
return `postId: ${this.id}, postTitle: ${this.title}.`;
}
}
let post2: Post2 = new Post2(1, "title 1");
class PostB extends Post2 {
getPost() {
return this.title;
}
}
종류 | 설명 |
---|---|
public | default값이며 어디서나 접근가능 |
protected | 클래스 내, 상속받은 자식 클래스에서 접근가능 |
private | 클래스 내에서만 접근 가능 |
생성자에 접근제어자 넣어 수정
class Post {
constructor(
public id: number = 0, // public은 생략할 수없고 default값 넣을수있다.
protected title: string
) {}
getPost() {
return `postId: ${this.id}, postTitle: ${this.title}.`;
}
}
13. Generic 기본
- Generic을 사용하면 재사용성이 높은 함수와 클래스를 생성할 수 있다.
- any처럼 타입을 직접 지정하지 않지만, 타입을 체크해 컴파일러 오류를 찾을 수 있게 된다.
*👉함수에 다른 타입의 인수가 계속해서 들어온다면? *
배열의 경우
// function getArrayLength(arr: number[] | string[] | boolean[] ): number {
// return arr.length;
// }
function getArrayLength<T>(arr: T[]): number {
return arr.length;
}
const array1 = [1, 2, 3];
const array2 = ['a', 'b', 'c'];
const array3 = [true, false, true];
getArrayLength<number>(array1);
getArrayLength<string>(array2);
getArrayLength<boolean>(array3);
any를 사용하지 않고 generic쓰기
Before
interface Vehicle {
name: string;
color: string;
option: any; // any 를 쓰지않고 타입처리하는방법?
}
const car: Vehicle = {
name: 'Car',
color: 'red',
option: {
price: 1000
}
}
const bike: Vehicle = {
name: 'Bike',
color: 'green',
option: true
}
After
interface Vehicle<T> {
name: string;
color: string;
option: T;
}
const car: Vehicle<{ price: number }> = {
name: 'Car',
color: 'red',
option: {
price: 1000
}
}
const bike: Vehicle<boolean> = {
name: "Bike",
color: "green",
option: true,
};
14. 좀 더 다양한 상황에서 Generics 를 사용해보기
👉함수에 매개변수가 두개라면?
const makeArr = <X, Y>(x: X, y: Y): [X, Y] => {
return [x, y];
}
const array = makeArr<number, number>(4, 5);
const array2 = makeArr("a", "b");
const array3 = makeArr<string, number>("a", 3);
// default
const makeArr1 = <X, Y = string>(x: X, y: Y): [X, Y] => {
return [x, y];
};
extends를 사용한 generics
꼭 넣어야하는 속성을 넣을 수 있음
// before
const makeFullName = (obj: {firstName: string, lastName: string}) => {
return {
...obj,
fullName: obj.firstName + " " + obj.lastName,
};
}
makeFullName({firstName: "John", lastName: "Doe", location: "Seoul"});
// after
const makeFullName2 = <T extends {firstName: string, lastName: string}>(obj: T) => {
return {
...obj,
fullName: obj.firstName + " " + obj.lastName,
};
};
makeFullName2({ firstName: "John", lastName: "Doe", location: "Seoul" });
makeFullName2({ lastName: "Doe", location: "Seoul" }); // error: firstName은 필수
리액트에서 Generic
// 컴포넌트에서 props로 뭐가 올지 모를때 generic사용
interface Props {
name: string;
}
const ReactComponent: React.FC<Props> = ({ name }) => { // Functional Component
const [state] = useState<{name: string | null}>({name: ""}); // useState에서도 generic사용
state.name
return <div>{name}</div>
}
15. Utility Type
Partial
파셔 타입은 특정 타입의 부분집합을 만족하는 타입을 정의할 수 있다.
interface Address {
email: string;
address: string;
}
const me: Partial<Address> = {};
const you: Partial<Address> = { email: "john@gmail.com" };
const youAndMe: Address = { email: "john@gmail.com", address: "john" };
Pick
특정 타입에서 몇개의 속성을 선택하여 타입을 정의한다.
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
const todo: TodoPreview = {
title: "Clean Room",
completed: false,
};
Omit
특정 속성만 제거한 타입을 정의한다. Pick의 반대.
interface Todo2 {
title: string;
description: string;
completed: boolean;
createdAt: number;
}
type TodoPreview2 = Omit<Todo2, "description">;
const todo2: TodoPreview2 = {
title: "clean room",
completed: false,
createdAt: 123123,
};
Exclude
일반 Union 유형을 전달한 다음 두번째 인수에서 제거할 멤버를 지정한다.
type myUnionType = "🍈" | "🍉" | "🍊" | "🍋";
let lemon: myUnionType = "🍋";
let noLemonsPlease: Exclude<myUnionType, "🍋"> = "🍈";
let noLemonsOrMelons: Exclude<myUnionType, "🍋" | "🍈"> = "🍉";
Required
원래 유형이 일부 속성을 선택 사항으로 정의한 경우에도 객체에 Required 속성이 있는지 확인해야 하는 경우가 있다.
type User = {
firstName: string,
lastName?: string,
}
let firstUser: User = {
firstName: "John"
}
let secondUser: Required<User> = {
firstName: "John"
// lastName 있어어한다.
}
Record<Keys, Type>
속성 키가 Keys이고 속성 값이 Type인 객체 type을 구성한다.
이 유틸리티는 type의 속성을 다른 type에 매핑하는데 사용할 수 있다.
interface CatInfo {
age: number;
breed: string;
}
type CatName = "miffy" | "boris" | "mordred";
const cats: Record<CatName, CatInfo> = {
miffy: { age: 10, breed: "Persian" },
boris: { age: 5, breed: "Maine Coon" },
mordred: { age: 16, breed: "British ShortHair" },
};
cats.boris;
ReturnType<T>
함수 T의 반환타입으로 구성된 타입을 만든다.
type T0 = ReturnType<() => string>;
type T1 = ReturnType<(s: string) => string>;
function fn(str: string) {
return str;
}
const a: ReturnType<typeof fn> = 'Hello';
const b: ReturnType<typeof fn> = true; // error
16. Implements vs Extends
Extends
Extends 키워드는 자바스크립트에서도 사용할 수 있으며
부모클래스에 있는 프로퍼티나 메소드를 상속해서 사용할 수 있게 만들어준다.
Implements
Implements 키워드는 새로운 클래스의 타입체크를 위해 사용되며
그 클래스 모양을 정의할때 사용한다.(부모클래스의 프로퍼티와 메소드를 상속받아 사용하는게 아니다.)
class Car {
mileage = 0;
price = 1000;
color = 'white';
drive() {
return 'drive!';
}
brake() {
return 'stop!';
}
}
// extends로는 모두 상속받기에 가능
class Ford extends Car {
}
const myFordCar = new Ford();
// implements
interface Part {
seats: number;
tire: number;
}
class Ford2 implements Car, Part { // 지정한 속성을 넣어야한다.
mileage = 0;
price = 2000;
color = "red";
seats = 2;
tire = 4;
drive() {
return "drive!";
}
brake() {
return "stop!";
}
}
const myFordCar2 = new Ford2();
17. Keyof operator
keyof연산자는 제공된 타입의 키를 추출하여 새로운 Union 유형으로 반환한다.
interface IUser {
name: string;
age: number;
address: string;
}
type UserKeys = keyof IUser; // 새로운 Union유형 반환
// "name" | "age" | "address"
typeof keyof
typeof연산자와 함께 사용하면 keyof연산자는 객체의 모든 키에 대한 union형식을 반환한다.
const user = {
name: "John",
age: 20,
address: 'seoul'
}
// type으로 변환후 keyof로 사용
type UserKeys2 = keyof typeof user;
enum의 key 추출
enum UserRole {
admin,
manager
}
type UserRoleKey = keyof typeof UserRole;
18. Mapped Types
맵드 타입을 이용하면 중복을 피하기 위해서 다른 타입을 바탕으로 새로운 타입을 생성할 수 있다.
Mapped type을 사용하는 것은 type이 다른 type에서 파생되고 동기화상ㅇ태를 유지하는 경우에 특히 유용하다.
Array.prototype.map()의 대표적인 예이다.
[1,2,3].map(value => value.toString());
여기에서 배열의 각 숫자를 가져와서 문자열로 변환했다.
이것처럼 Typescript에서 Mapped Type은 하나의 Type을 가져와 각 속성에 변환을 적용하여 다른 type으로 변환한다는 의미이다.
Mapped Type 사용해보기
type DeviceFormatter<T> = {
[K in keyof T]: T[K];
}
type Device = {
manufacturer: string;
price: number;
};
const iphone: DeviceFormatter<Device> = {manufacturer: 'apple', price: 100};
// 만약 객체에서 가격이나 제조사만 가지거나
// 둘다 가지지 않는게 있을 경우에는?
// Before
const priceOnly: DeviceFormatter<Device> = { price: 23 };
const manufacturerOnly: DeviceFormatter<Device> = { manufacturer: 'apple' };
const empty: DeviceFormatter<Device> = {};
// After
type DeviceFormatter2<T> = {
[K in keyof T]? : T[K]; // optional하게 만들어준다.
}
const priceOnly2: DeviceFormatter2<Device> = { price: 23 };
const manufacturerOnly2: DeviceFormatter2<Device> = { manufacturer: "apple" };
const empty2: DeviceFormatter2<Device> = {};
19. tsconfig.json 파일 더 자세히 알아보기
디렉터리에 tsconfig.json파일이 있으면 해당 디렉터리가 typescript프로젝트의 루트임을 나타낸다.
tsconfig.json파일은 프로젝트를 컴파일하는데 필요한 루트파일과 컴파일러 옵션을 지정한다.
전체구성
{
"compilerOptions": { },
// 컴파일할 개별 파일 목록
"files": [
"node_modules/library/index.ts"
],
// 컴파일러를 이용해서 변환할 폴더 경로를 지정
"include": [
"./src/**/*.ts"
],
// 컴파일러를 이용해서 변환하지 않을 폴더 경로를 지정
"exclude": [
"node_modules",
"dist"
],
// 상속해서 사용할 다른 TS 구성파일 지정
"extends": "main_config.json"
}
compilerOption
"compilerOptions": {
"rootDir": "./src", // 소스 파일 안에서 root폴더 명시
"outDir": "./build/js", // ts 컴파일 후에 어느 경로로 들어가는지 명시
"target": "ES2015", // 타입스크립트를 ES2015버전의 자바스크립트로 변환
"noEmitOnError": false, // 파일에 에러가 있을 때에는 컴파일 하지 않는 옵션
"module": "ESNext", // 컴파일을 마친 후 자바스크립트가 사용한은 모듈 시스템
"esModuleInterop": true, // ESM(ES Module), CommonJS 호환해서 사용 가능
"moduleResolution": "Node",
"lib": ["ESNext", "DOM"], // 컴파일 과정에서 사용하는 라이브러리 지정
"declaration": true, // d.ts파일 생성 타입들만 따로 관리할 수 있다.
"baseUrl": "./", // tsconfig.json과 동일한 폴더에서 시작하는 파일을 먼저 찾는다
"path": {
// 임포트 경로 설정
"@src/*": ["src/*"]
},
"isolatedModules": true, // 프로젝트 내 각 소스파일을 모듈로 만들기 강제
"removeComments": true, // 컴파일시 타입스크립트 소스의 주석을 모두 제거
"allowJs": true, // js파일과 ts파일을 같이 사용
"checkJs": true, // js파일에서도 type check
"strict": true, // 타입스크립트 파일에 타입을 엄격하게 사용한다고 명시
"noImplicitAny": true, // 명시적이지 않은 any유형으로 표현식 및 선언 사용시 오류 발생
"strictNullChecks": true, // 엄격한 null검사 사용
"strictFunctionTypes": true, // 엄격한 함수 유형 검사 사용
"strictBindCallApply": true, // 엄격한 bind, call, apply 함수 메서드 사용
"strictPropertyInitialization": true, // 클래스에서 속성 초기화 엄격 검사 사용
"noImplicitThis": true, // 명시적이지 않은 any유형으로 this표현식 사용시 오류발생
"alwaysStrict": true // 엄격모드에서 구문 분석후, 각 소스파일에 "use strict"코드 출력
},
lib
target에 어떤 값을 지정하느냐에 따라 다른 기본값을 가진다.
target es3 -> 기본값 lib.d.ts
target es5 -> 기본값 dom, es6, scripthost
만약 lib를 직접 지젛아면 배열안에 있는 라이브러리만 사용한다."lib": ["ESNext", "DOM"]
moduleResolution
타입스크립트 컴파일러가 모듈을 찾는 방법
- 먼저 모듈 import가 상대인지 비상대 import인지 구분한다.
- Classic또는 Node 전략 중 하나를 이용해서 컴파일러에서 moduleA를 찾을 위치를 알려준다.
- Classic : Typescript 1.6이전에 기본값
- Node : 현재 기본값이며 modern code에서는 이 전략만 사용
baseUrl
이 프로퍼티가 지정되어 있다면 비상대적 import의 모듈 해석 과정에 하나의 과정을 추가합니다."baseUrl" : "./"
이라고 사용하면 tsconfig.json와 동일한 폴더에서 시작하는 파일을 먼저 찾는다.
path
src/foo/a/b/index.ts에서
src/bar/index.ts에 있는 함수를 import해오려면?
path 설정 전:import { bar } from '../../../../bar'
path 설정 후:
"path": {
"@src/*": [
"src/*"
]
}
import { bar } from '@src/bar'
라고 사용할 수 있다.
isolationModules
true로 설정하면 프로젝트 내에서 모든 각각의 소스코드 파일을 모듈로 만들기를 강제한다. 소스코드 파일에서 import 또는 export를 사용하면 그 파일은 모듈이 되지만 만약 import / export를 하지 않으면 그 파일은 전역 공간으로 정의되고 모듈이 아니기에 에러가 나게 된다.
이때는 export{}
라고 적어야 에러가 없다.
removeComments
컴파일시 타입스크립트 소스의 주석을 모두 제거하는 설정
allowJS
js파일을 같이 사용한다.
checkJs
allowJS와 함께 작동한다.
checkJs가 활성화되면 Javascript파일에 오류가 보고된다.
이는 프로젝트에 포함된 모든 Javascript파일의 맨 위에 //@ts-check
를 포함하는 것과 같다.
js파일에서도 타입을 체크하게된다."checkJs": true,
forceConsistentCasingInFileNames
파일의 이름을 대문자로 판별하게 하는 옵션이다.(대소문자구별)import {B} from './Test'
declaration
이 옵션을 true로 하면 TS파일을 JS로 컴파일하는 과정에서 JS파일과 함께 d.ts선언 파일이 생성된다.
선언파일에서 타입들만 따로 관리할 수 있다.
strict
"strict": true, // 타입스크립트 파일에 타입을 엄격하게 사용한다고 명시
"noImplicitAny": true, // 명시적이지 않은 any유형으로 표현식 및 선언 사용시 오류 발생
"strictNullChecks": true, // 엄격한 null검사 사용
"strictFunctionTypes": true, // 엄격한 함수 유형 검사 사용
"strictBindCallApply": true, // 엄격한 bind, call, apply 함수 메서드 사용
"strictPropertyInitialization": true, // 클래스에서 속성 초기화 엄격 검사 사용
"noImplicitThis": true, // 명시적이지 않은 any유형으로 this표현식 사용시 오류발생
"alwaysStrict": true // 엄격모드에서 구문 분석후, 각 소스파일에 "use strict"코드 출력
'Frontend > Javascript・Typescript' 카테고리의 다른 글
Node.js란(등장배경/내부구조/작동방식) (5) | 2024.07.23 |
---|---|
[Typescript] 단언 연산자 느낌표 !(타입단언) (0) | 2024.02.08 |
[Typescript] Typescript Essentials2 (0) | 2023.11.01 |
[Typescript] Typescript Essentials1 (1) | 2023.11.01 |
[Jquery]사이즈에 따른 반응형 스크립트 / colspan setAttribute (0) | 2021.12.15 |