폴리필(polyfill)과 트랜스파일(transpile)
최신 기능을 구버전의 실행 환경에서 동작할 수 있게 바꿔주는 역할
- 폴리필
브라우저가 지원하지 않는 코드를 브라우저에서 사용할 수 있도록 변환한 코드 조각이나 플러그인을 말함
예) core.js, pollyfill.io- 트랜스 파일
최신 버전 코드를 예쩐 버전의 코드로 변환하는 과정을 말함
예) babel
컴포넌트 베이스 개발 (Component Based Development, CBD)
@ts-check
를 추가하면 타입 및 에러 확인이 가능하며 자바스크립트 소스코드에 타입힌트를 제공하는 HTML 문서를 생성할 수 있음Superset
- 기존 언어에 새로운 기능과 문법을 추가해서 보완하거나 향상하는 것
- 슈퍼셋 언어는 기존 언어와 호환되며 일반적으로 컴파일 등으로 기존 언어 코드로 변환되어 실행됨
타입스크립트 interface
- 객체 구조를 정의하는 역할
- 특정 객체가 가져야 하는 속성과 메서드의 집합을 인터페이스로 정의해서 객체가 그 구조를 따르게 함
비동기 처리를 다룰 때 고려사항
- 현재 비동기 동작이 어떤 상태인가?
- 비동기 동작을 위해 필요한 정보가 무엇인가?
- 요청이 성공했다면 받아온 정보를 어떻게 저장하고 관리할 것인가?
- 요청이 실패했다면 실패에 대한 정보를 어떻게 확인할 것인가?
- 비동기 요청에 대한 코드를 쉽게 유지보수할 수 있도록 어떻게 구조화하고 관리할 것인가?
node ./node_modules/typescript/bin/tsc index.ts
// @ts-check
/**
* @description 두 수의 합을 구하는 함수
* @param {number} a 첫 번째 숫자
* @param {number} b 두 번째 숫자
*/
function sum(a, b){
return a + b;
튜플 타입 (tuple)
- 튜플은 특정 형태를 갖는 배열을 의미
- 배열의 길이가 고정되고 각 요소 타입이 정의된 배열
var items: [string, number] = ['hi', 11];
null과 undefined
- null과 undefined 타입은 타입스크립트 설정 파일의 strict 옵션에 따라서 사용 여부가 결정됨
- strict 옵션이 꺼져 있을 때는 신경 쓰지 않아도 되는 타입!
상속
상속이란 객체 간 관계를 형성하는 방법
상위(부모) 클래스의 비용을 하위(자식) 클래스가 물려받아 사용하거나 확장하는 기법
class Persion {
constructor(name, age){
this.name = name;
this.age = age;
}
logAge() {
console.log(this.age);
}
}
class Developer extends Person {
constructor(name, age, skill) {
super(name, age);
this.kill = skill;
}
logDeveloperInfo() {
this.logeAge();
console.log(this.name);
console.log(this.skill);
}
}
interface Person {
name: string;
age: number;
}
interface Developer extends Person {
skill: string;
}
var ironman: Developer = {
name: "ironman",
age: 21,
skill: "ironning",
power: true
}
인덱싱
객체의 특정 속성을 접근하거나
배열의 인덱스로 특정 요소에 접근하는 동작을 의미var user = { name: 'jek', admin: true, }; console.log(user['name']); // jek var companies = ['삼성', '네이버', '구글']; console.log(companies[0]); // 삼성
interface StringArray {
[index: number] : string;
}
var companies: StringArray = ['samsung', 'naver', 'google'];
interface ScoreMap {
[level: string]: number;
}
var score: ScoreMap = {
good: 80
};
인덱스 시그니처 (index signature)
- 정확히 속성 이름을 명시하지 않고 속성 이름의 타입과 속성의 값의 타입을 정의하는 문법
interface ScoreMap {[level: string]: string;}
- 객체의 속성과 이름과 속성 값이 정해져 있는 경우에는 속성 이름과 속성 값 타입을 명시해서 정의하고, 속성 이름은 모르지만 속성 이름의 타입과 값의 타입을 아는 경우에는 인덱스 시그니처를 활용
function logText(text: string | number) {
console.log(text);
}
function logText(text: string | number) {
if(typeof text === 'string') {
console.log(text.toUpperCase());
}
if(typeof text === 'number') {
console.log(text.toLocalString());
}
console.log("");
}
타입 가드
- 타입을 구분한 후 코드를 작성하는 것
interface Avenger {
name: string;
}
interface Hero {
skill: string;
}
function introduce(someome: Avenger & Hero) {
console.log(someone.name);
console.log(someone.skill);
}
// type MyName = string;
// var capt: string = 'jek';
type MyName = string;
var capt: MyName = 'jek';
/** 타입 별칭 사용전
function logText(text: string | number) {
// ...
}
var message: string | number = '안녕하세요';
logText(message);
**/
// 타입 별칭 사용후
// string | number 타입을 MyMessage 라는 타입 별칭으로 정의하고,
// 반복되는 코드를 줄였을 뿐 아니라
// string|number 타입이 내 메시지에 사용되는 타입이라는 의미도 부여함!
// 타입 별칭을 사용하면 타입에 의미를 담아 여러 곳에 재사용할 수 있음!
type MyMessage = string | number;
function logText(text:Message){
// ...
}
var message: MyMessage = '안녕하세요';
logText(message);
타입 확장
- 타입 확장이란 이미 정의되어 있는 타입들을 조합해서 더 큰 의미의 타입을 생성하는 것
- 타입 별칭과 인터페이스는 타입을 확장하는 방식이 다름
* 인터페이스 => 상속이라는 개념사용하여 확장
- 별칭 => 인터섹션 타입으로 객체 타입을 2개 합쳐서 사용
인터페이스의 '선언 병합'(declaration merging)
- 인터페이스는 동일한 이름으로 인터페이스를 선언하면 인터페이스 내용을 합치는 특성이 있음
interface에는 x in dict 타입 사용 불가
2021년 이전의 타입스크립트 공식문서에서는 '좋은 소프트웨어는 확장이 용이해야 한다(open-closed principle)'는 관점에서 타입 별칭보다 인터페이스의 사용을 권장했음
하지만 2023년 공식 문서에는 이 내용이 없고, 일단 인터페이스를 주로 사용해보고 타입 별칭이 필요할 때 타입 별칭을 쓰라고 안내함
실제로 두 문법을 사용하다 보면 '타입 별칭으로만 타입 정의가 가능한 곳에서는 타입 별칭을 사용하고 백엔드와의 인터페이스를 정의하는 곳에는 인터페이스를 이용하자'는 결론이 나온다고함! (쉽게 시작하는 타입스크립츠 책 저자 왈)
type MyString = string;
type StringOrNumber = string | number;
type Admin = Person & Developer;
~~~
// 제네릭
type Dropdown<T> = {id: string; title: T;};
// 유틸리티 타입
type Admin = {name: string; age: number; role: string;};
type OnlyName = Pick<Admin, 'name'>
// 맵드 타입
type Picker<T, K extends keyof T> = {[P in K]: T[P];}
* 백엔드와의 **인터페이스** 정의
* 백엔드/프론트엔드 간에 어떻게 데이터를 넘길지 정의하는 작업을 '인터페이스를 정의한다'라고 함
* 여기에서 인터페이스는 영역 간 접점(데이터)을 서로 맞추는 작업을 의미함
* 백엔드에서 전달받은 데이터의 타입을 정의할 때 타입스크립트 인터페이스를 사용한다면 (타입 별칭을 사용할 수는 있지만) 기존 타입의 확장이라는 측면에서 인터페이스로 정의하는 것이 더 수월함
* 유연하게 타입을 확장하는 관점에서는 타입 별칭보다 인터페이스가 더 유리함
이넘은 C, JAVA 등 다른 프로그래밍 언어에도 있는 데이터 타입
타입스크립트에서는 이넘을 지원함 (자바스크립트에는 없음)
이넘은 특정 값의 집합을 의미하는 데이터 타입, 상수 집합이로고도 표현함
상수 (constant)
변하지 않는 고정 값을 '상수'라고 함
상수는 단순히 고정된 값을 저장하는 것뿐만 아니라 이 값이 어떤 의미를 갖는지 알려줌으로써 가독성을 높이는 장점이 있음
보통 모두 대문자로 작성해서 일반 변수와 구분함
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
console.log(Direction.up); // 0
// math.js
function sum(a, b) {
return a + b;
}
module.exports = {
sum
};
// app.js
var math = require('./math.js');
console.log(math.sum(10, 20)); // 30
<html>
<head></head>
<body>
<!-- 라이브러리 파일 다운로드 후 다음과 같이 연결 -->
<script src="require.js"></script>
<script>
require(["https://unpkg.com/vue@3/dist/vue.global.js "], function() {
console.log("vue is loaded");
});
</script>
</body>
</html>
// math.js
function sum(a, b) {
return a + b;
}
export { sum }
// app.js
import { sum } from "./math.js";
console.log(sum(10, 20));
// math.js
function sum(a, b) {
return a + b;
}
export default sum;
// app.js
import sum from "./math.js";
console.log(sum(10, 20));
// math.js
function sum(a, b) {
return a + b;
}
export { sum }
// app.js
import { sum as add } from "./math.js";
console.log(add(10, 20));
// math.js
function sum(a, b) {
return a + b;
}
function substract(a, b) {
return a - b;
}
function divide(a, b) {
return a / b;
}
export { sum, substract, divide }
// app.js
import * as myMath from "./math.js"
console.log(myMath.sum(10, 20)); // 30
console.log(myMath.subtract(30, 10)); // 20
console.log(myMath.divide(4, 2)); // 2
// hero.ts
interface Hulk {
name: string;
skill: string;
}
export { Hulk };
// app.ts
import type { Hulk } from "./hero";
var banner: Hulk = {
name: "배너",
skill: "화내기",
};
// hero.ts
interface Hulk {
name: string;
skill: string;
}
function smashing() {
return '';
};
var doctor = {
name: "스트레인지"
};
export { Hulk, smashing, doctor };
// app.ts
import type { type Hulk, doctor, smashing } from "./hero";
var banner: Hulk = {
name: "배너",
skill: "화내기",
};
// ./hero/hulk.ts
interface Banner {
name: string;
}
export { Banner }
// ./hero/ironman.ts
interface Tony {
name: string;
}
export { Tony }
// ./hero/captain.ts
interface Steve {
name: string;
}
export { Steve }
// ./hero/index.ts
import { Banner } from './hulk';
import { Tony } from './ironman';
import { Steve } from './captain';
export { Banner, Tony, Steve };
// app.ts
// import { Banner } from "./hero/hulk";
// import { Tony } from "./hero/ironman";
// import { Steve } from "./hero/captain";
import { Banner, Tony, Steve } from "./hero";
var banner: Banner = { name: "배너" };
var tony: Tony = { name: "토니" };
var steve: Steve = { name: "스티브" };
// tsconfig.json
// * 최신 자바스크립트 문법을 의미하는 ESNext를 추가
{
"compilerOptions": {
"lib": ["ESNext"]
}
}
interface Profile {
id: string;
address: string;
}
type ProfileId = Pick<Profile, "id">;
let captainProfile: ProfileId = {
id: "캡틴 아이디",
};
console.log(captainProfile);
interface UserProfile {
id: string;
name: string;
address: string;
}
type HulkProfile = Pick<UserProfile, "id" | "name">;
let hulk: HulkProfile = {
id: "1",
name: "hulk",
};
Pick<대상타입, '대상타입의 속성 이름'>
Pick<대상타입, '대상타입의 속성 1 이름' | '대상타입의 속성 2 이름'>
Omit<대상타입, '대상타입 속성 이름'>
Omit<대상타입, '대상타입의 속성 1 이름' | '대상타입의 속성 2 이름'>
interface UserProfile {
id: string;
name: string;
address: string;
}
type User = Omit<UserProfile, "address">;
let user = {
id: "1",
name: "사용자",
};
Partial<대상타입>
interface Todo {
id: string;
title: string;
}
type OptionTodo = Partial<Todo>;
Exclude<대상유니언타입, '제거할 타입이름'>
Exclude<대상유니언타입, '제거할 타입이름 1' | 제거할 타입이름 2'>
type Languages = "C" | "Java" | "TypeScript" | "React";
type TrueLanguages = Exclude<Languages, "React">;
type WebLanguages = Exclude<Languages, "C" | "JAVA" | "React">;
type HeroProfile = {
skill: string;
age: number;
};
type HeroNames = "thor" | "hulk" | "capt";
type Heroes = Record<HeroNames, HeroProfile>;
let avengers: Heroes = {
thor: {
skill: "shield",
age: 100,
},
hulk: {
skill: "hammer",
age: 3000,
},
capt: {
skill: "shouting",
age: 47,
},
};
type PhoneBook = Record<string, string>;
let familyPhones: PhoneBook = {
dad: "010-1111-1111",
mom: "010-2222-2222",
};
Record<객체속성 키로 사용할 타입, 객체 속성의 값으로 사용할 타입>
type HeroNames = "thor" | "hulk" | "capt";
type HeroAttendance = {
[Name in HeroNames]: boolean;
};
interface Hero {
name: string;
skill: string;
}
type HeroPropCheck1 = {
[H in keyof Hero]: boolean;
};
type HeroPropCheck2 = {
[H in "name" | "skill"]: boolean;
};
+, -. ?, readonly
type Hero = {
name: string;
skill: string;
};
type HeroOptional = {
[H in keyof Hero]?: string;
};
type HeroRequired<T> = {
[Property in keyof T]-?: T[Property];
};
let capt: HeroRequired<HeroOptional> = {
name: "captain",
skill: "throwing shield",
};
interface Todo {
id: string;
title: string;
}
type MyPartial = {
[Property in keyof Todo]?: Todo[Property];
};
// 제네릭으로 넘겨받은 타입의 속성을 모두 옵션 속성으로 변환
type MyPartialV2<Type> = {
[Property in keyof Type]?: Type[Property];
};
type TodoPartial = MyPartialV2<Todo>;
매개변수 vs. 인자
- 매개변수 (parameter)
- 함수를 정의할 때 사용되는 변수
- 인자 (argument)
- 함수를 호출할 때 전달되는 값
// tsconfig.json
{
"compilerOptions": {
"target": "es5"
}
}
tsc index.ts --noEmitOnError
// tsconfig.json
{
"complierOptions": {
"noEmitOnError": true
},
"files": ["index.ts"]
}
npx typescript index.ts --noEmitOnError
yarn add typescript
tsc --init
npx typescript tsc --init
npx (Node Package eXecute)
- NPM 패키지를 설치하지 않고도 실행할 수 있게 하는 도구
// tsconfig.json
{
"files": ["index.ts", "main.ts", "utils.ts"]
}
// tsconfig.json
// src 폴더 아래에 있는 모든 파일과 tests 폴더 아래 spec.ts 확장자를 가진 모든 파일을 컴파일 대상으로 지정하겠다는 의미
{
"include": ["src/*", "tests/*.spec.ts"]
}
와일드 카드(*)
- 프로그램에서 흔히 쓰는 검색 패턴
*
: 디렉터리 구분자를 제외한 모든 파일 이름**/
: 해당 폴더의 모든 하위 폴더
// tsconfig.json
// src 폴더 아래에 있는 모든 파일(하위 폴더의 파일까지 모두 포함)과 utils 폴더 바로 아래에 있는 모든 파일을 의미
{
"include": ["src/**/*", "utils/*"]
}
// tsconfig.json
{
"include": ["**/*"]
}
// tsconfig.json
{
"exclude": ["node_modules", "tests/**/*"]
}
설정 파일을 공통으로 사용하거나 빌드용 타입스크립트 설정을 분리하고 싶을 때 사용
상대 경로로 지정하여 설정 파일을 불러올 수 있음
예시) base.json 파일에 기본적인 컴파일러 옵션들을 정의해두고 tsconfig.json 파일에서 extends 속성으로 상속 받음
// base.json
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "esnext"]
}
}
// tsconfig.json
{
"extends": "./base",
"compilerOptions": {
"strict": true
}
}
프레임워크, 라이브버리 별 공통 설정 파일
// tsconfig.json
{
"compilerOptions": {
"lib": ["DOM", "ESNext"]
}
}
// main.ts
document.querySelector("#app");
{
"compilerOptions": {
"strict": true,
// strict가 true이면 아래의 속성 목록을 모두 켠 것과 같음
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictBindCallApply": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"alwaysStrict": true,
"useUnknownInCatchVariables": true
}
}
typeof null; // 'object'
typeof undefined; // 'undefined'
function App() {
return <div>Hello React</div>
}
import React from 'react';
export const App = () => <div>Hello React</div>
import React from 'react';
export const App = () => React.createElement("div", null, "Hello React");
import {jsx as _jsx} from "react/jsx-runtime";
import React from 'react';
export const App = () => _jsx("div", {children: "Hello React"});
import {jsxDev as _jsxDev} from "react/jsx-dev-runtime";
const _jsxFileName = "/home/runner/work/TypeScript-Website/TypeScript-Website/index.tsx"
import React from 'react';
export const App = () => _jsxDev("div", {children: "Hello React"}, void 0, false, {filename: _jsxFileName, lineNumber: 9, columnNumber: 32}, this);
import React from 'react';
export const App = () => <div>Hello React</div>
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./src"
}
}
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"api": ["api/*"]
}
}
}
// LoginInfo.vue
import { fetchUser } from 'api/users';
// tsconfig.json
{
"compilerOptions": {
"removeComments": false
}
}
// webpack.config.js
const path = require("path");
module.exports = {
entry: './src/index.ts',
module: {
rules: [
{
test: /\.test?$/,
use: 'ts-loader',
exclude: /node_modules/,
}
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
}
@types/라이브러리명
라고 입력타입 선언 파일을 생성해 주는 명령어를 활용
이 명령어는 대상 라이브러리의 규모가 작고 간단할 때 사용하면 좋음
npx -p typescript tsc <대상 파일명> --declaration --allows --emitDeclarationOnly --outDir types