TypeScript 설치 및 실행
TypeScript를 설치하는 방법은 npm 또는 yarn을 사용하는 것입니다.
npm install typescript -g
yarn global add typescript
설치 후 TypeScript 파일을 JavaScript로 컴파일하거나 직접 실행할 수 있습니다.
tsc Demo1.ts
npm install -g ts-node
ts-node Demo1.ts
정적 타입
TypeScript의 핵심은 정적 타입 시스템입니다. 변수 선언 시 타입을 명시하면 해당 변수의 타입은 고정되며, 타입에 맞는 속성과 메서드만 사용할 수 있습니다.
const count: number = 1;
기본 및 객체 타입
TypeScript는 다양한 기본 타입을 지원합니다. 변수명 뒤에 콜론(:)을 붙여 타입을 명시할 수 있습니다.
- 기본 타입:
number,string,null,undefined,symbol,boolean,void
const count: number = 918;
객체 타입은 배열, 클래스, 함수 등 다양한 형태로 정의될 수 있습니다.
const arr: string[] = ["sds", "sdsd", "sdw"];
const func: () => string = () => { return "sdsd"; };
타입 주석과 타입 추론
TypeScript는 타입 주석(Type Annotation)을 통해 명시적으로 타입을 지정할 수 있으며, 타입 추론(Type Inference)을 통해 컴파일러가 타입을 자동으로 유추하도록 할 수도 있습니다.
function getTotal(one: number, two: number) {
return one + two;
}
const total = getTotal(1, 2); // 'total'의 타입은 number로 자동 추론됩니다.
타입 추론이 가능한 경우 타입을 생략하여 코드를 간결하게 만들 수 있습니다. 명확하지 않은 경우 타입 주석을 사용하여 타입을 명확히 하는 것이 좋습니다.
함수 매개변수 및 반환 타입 정의
함수의 매개변수와 반환 값에 타입을 지정하여 함수의 안정성을 높일 수 있습니다.
// 반환 값이 있는 함수
function getTotal(one: number, two: number): number {
return one + two;
}
const total = getTotal(1, 2);
// 반환 값이 없는 함수 (void)
function sayHello(): void {
console.log('hello');
}
// never 타입: 함수가 정상적으로 종료되지 않음을 나타냅니다 (예: 오류 발생 또는 무한 루프).
function errFunc(): never {
throw new Error();
// console.log('hello~'); // 이 코드는 실행되지 않습니다.
}
function forNever(): never {
while (true) {}
// console.log('hl-'); // 이 코드는 실행되지 않습니다.
}
// 객체 구조 분해 할당을 매개변수로 사용
function add({ one, two }: { one: number, two: number }): number {
return one + two;
}
const total = add({ one: 1, two: 2 });
function getNumber({ one }: { one: number }): number {
return one;
}
const num = getNumber({ one: 8 });
배열 타입 정의
배열의 요소 타입을 지정하는 일반적인 방법과 객체 배열 타입을 정의하는 방법을 알아봅니다.
// 일반 배열 타입
const numberArr: number[] = [1, 2, 3];
const strArr: string[] = ['1', '2'];
const undefinedArr: undefined[] = [undefined, undefined];
const mixedArr: (number | string)[] = [1, '2', 3]; // 숫자 또는 문자열 배열
// 배열 내 객체 타입 정의
const objArr: { name: string, age: number }[] = [
{ name: 'ssd', age: 12 },
{ name: 'sdsd', age: 123 },
];
// 타입 별칭(Type Alias)을 사용하여 코드 재사용
type PersonItem = { name: string, age: number };
const arrWithTypeAlias: PersonItem[] = [
{ name: 'ds', age: 12 },
{ name: '233', age: 8 },
];
// 클래스를 배열 타입으로 사용
class PersonClass {
name: string;
age: number;
}
const arrWithClass: PersonClass[] = [
{ name: '12', age: 12 },
{ name: 'dfdf', age: 89 },
];
튜플 (Tuple)
튜플은 고정된 개수의 요소와 각 요소의 타입을 명확히 정의할 때 유용합니다. 일반 배열에서는 타입이 혼합될 때 유연성이 있지만, 튜플은 순서와 타입이 중요할 때 사용됩니다.
// 튜플: string, string, number 순서로 타입을 정의
const tupleArr: [string, string, number] = ['ss', 'sdsd', 18];
인터페이스 (Interface)
인터페이스는 객체의 구조를 정의하고 타입을 규정하는 데 사용됩니다. 반복되는 타입 정의를 묶어 재사용성을 높입니다.
interface Person {
name: string;
age: number;
height: number;
}
function getPersonInfo(person: Person) {
if (person.age < 24 && person.height > 160) {
console.log(person.name + ' is young and tall.');
} else if (person.age > 24 && person.height < 160) {
console.log(person.name + ' is older and short.');
}
}
// 인터페이스와 타입 별칭의 차이점:
// - 타입 별칭은 `type AliasName = string;` 과 같이 기본 타입에도 사용할 수 있지만,
// 인터페이스는 객체 구조에만 사용 가능합니다.
// - 인터페이스는 선택적 속성(`?`)을 정의할 수 있습니다.
interface OptionalPerson {
name: string;
age: number;
height?: number; // 선택적 속성
}
// 인터페이스는 추가 속성을 허용할 수 있습니다.
interface DynamicPerson {
name: string;
age: number;
height?: number;
[propName: string]: any; // 인덱스 시그니처: 문자열 키에 대해 어떤 타입이든 허용
}
const personWithExtra: DynamicPerson = {
name: 'kk',
age: 12,
sex: 'female', // 추가 속성
};
// 인터페이스는 메서드도 정의할 수 있습니다.
interface MethodPerson {
name: string;
age: number;
height?: number;
[propName: string]: any;
say(): string; // 메서드 정의
}
const personWithMethod: MethodPerson = {
name: 'kk',
age: 13,
height: 190,
say() {
return 'hello~';
}
};
// 인터페이스와 클래스: 구현(implements) 및 상속(extends)
class XY implements Person {
name = 'k';
age = 13; // 타입 오류: string은 number에 할당될 수 없습니다.
height = 120;
say() {
return 'hello--';
}
}
interface Teacher extends Person {
teach(): string;
}
const personData: Teacher = {
name: 's',
age: 12,
height: 190,
say() {
return 'kk';
},
teach() {
return 'mm';
}
};
const getResume = (person: Teacher) => {
console.log('Resume for: ' + person.name);
};
클래스 (Class)
클래스는 객체 지향 프로그래밍의 기본 요소로, 속성과 메서드를 캡슐화합니다.
class Person {
content = "hi";
sayHi() {
return this.content;
}
}
const personInstance = new Person();
console.log(personInstance.sayHi());
// 클래스 오버라이딩: 자식 클래스가 부모 클래스의 메서드를 재정의
class XY extends Person {
sayHi() {
return 'hi, girl';
}
sayWorld() {
return 'hello world';
}
}
// super 키워드: 부모 클래스의 메서드 호출
class ExtendedPerson extends Person {
sayHi() {
return super.sayHi() + "---Hello World";
}
}
클래스 접근 제어자
public, protected, private 키워드를 사용하여 클래스 멤버의 접근 범위를 제어할 수 있습니다.
// public: 클래스 내부 및 외부에서 접근 가능 (기본값)
class PublicPerson {
public name: string;
public sayHello() {
console.log(this.name + ' says Hello');
}
}
const pubPerson = new PublicPerson();
pubPerson.name = 'jspang.com';
pubPerson.sayHello();
console.log(pubPerson.name);
// private: 클래스 내부에서만 접근 가능
class PrivatePerson {
private name: string;
public sayHello() {
console.log(this.name + ' says Hello'); // 내부 접근 가능
}
}
const privPerson = new PrivatePerson();
// privPerson.name = 'jspang.com'; // 외부 접근 시 오류 발생
privPerson.sayHello();
// console.log(privPerson.name); // 외부 접근 시 오류 발생
// protected: 클래스 내부 및 상속받은 자식 클래스에서 접근 가능
class ProtectedPerson {
protected name: string;
public sayHello() {
console.log(this.name + ' says Hello'); // 내부 접근 가능
}
}
class Teacher extends ProtectedPerson {
public sayBye() {
this.name; // 자식 클래스 내부 접근 가능
}
}
클래스 생성자
생성자(constructor)는 클래스의 인스턴스가 생성될 때 자동으로 호출되는 메서드입니다. 인스턴스 초기화에 사용됩니다.
// 생성자를 이용한 속성 초기화
class PersonConstructor {
public name: string;
constructor(name: string) {
this.name = name;
}
}
const personInstance1 = new PersonConstructor('sds');
console.log(personInstance1.name);
// 접근 제어자를 생성자 매개변수에 사용하여 속성 자동 생성
class PersonShort {
constructor(public name: string) {}
}
const personInstance2 = new PersonShort('kk');
console.log(personInstance2.name);
// 클래스 상속에서의 생성자
class ParentPerson {
constructor(public name: string) {}
}
class ChildTeacher extends ParentPerson {
constructor(public age: number) {
super('js'); // 부모 클래스의 생성자 호출
}
}
const teacherInstance = new ChildTeacher(19);
console.log(teacherInstance.age);
console.log(teacherInstance.name);
// 자식 클래스에서 생성자를 정의할 경우, 반드시 super()를 호출하여 부모 클래스의 생성자를 먼저 호출해야 합니다.
Getter/Setter/Static
getter와 setter는 객체의 속성에 접근하거나 값을 변경할 때 특정 로직을 실행하도록 하는 메서드입니다. static 멤버는 클래스의 인스턴스가 아닌 클래스 자체에 속합니다.
클래스의 읽기 전용 속성 및 추상 클래스
readonly 키워드는 속성을 선언 시에만 초기화할 수 있도록 하여 변경을 방지합니다. abstract 클래스는 추상 메서드를 포함하며, 직접 인스턴스화할 수 없고 반드시 상속받아 구현해야 합니다.
// readonly 속성
class ReadonlyPerson {
public readonly name: string;
constructor(name: string) {
this.name = name;
}
}
// abstract 클래스
abstract class AbstractSkill {
abstract skill(): void; // 추상 메서드
}
class SkillA extends AbstractSkill {
skill() {
console.log('Skill A implemented');
}
}
class SkillB extends AbstractSkill {
skill() {
console.log('Skill B implemented');
}
}
class SkillC extends AbstractSkill {
skill() {
console.log('Skill C implemented');
}
}
tsconfig.json
tsconfig.json 파일은 TypeScript 컴파일러의 설정을 정의합니다. tsc --init 명령어로 생성할 수 있습니다.
include: 컴파일할 파일을 지정합니다.exclude: 컴파일에서 제외할 파일을 지정합니다.compilerOptions: JavaScript로 변환하는 방식 등 다양한 컴파일 옵션을 설정합니다. (예:outDir,rootDir,target,module등)
주의: tsconfig.json 파일 내에서는 큰따옴표(")를 사용해야 합니다.
유니언 타입과 타입 가드
유니언 타입(Union Types)은 여러 타입 중 하나일 수 있는 변수를 정의할 때 사용합니다. (예: string | number). 타입 가드(Type Guards)는 유니언 타입의 변수가 어떤 타입인지 런타임에 확인하여 안전하게 접근하도록 돕습니다.
주요 타입 가드 기법:
- 타입 단언 (Type Assertion):
as키워드를 사용하여 개발자가 타입을 명시적으로 지정합니다. - `in` 연산자: 객체에 특정 속성이 있는지 확인합니다.
- `typeof` 연산자: 변수의 타입을 문자열로 반환합니다. (primitive 타입에 주로 사용)
- `instanceof` 연산자: 객체가 특정 클래스의 인스턴스인지 확인합니다.
interface Waiter {
isWaiter: boolean;
saySomething: () => {};
}
interface Teacher {
isTeacher: boolean;
teachSomething: () => {};
}
// 타입 단언 예시
function judgeWho(person: Waiter | Teacher) {
if (person.isWaiter) {
(person as Waiter).saySomething();
} else {
(person as Teacher).teachSomething();
}
}
// 'in' 연산자 예시
function processItem(item: Waiter | Teacher) {
if ('teachSomething' in item) {
item.teachSomething();
} else {
item.saySomething();
}
}
// typeof 연산자 예시
function concatenate(first: string | number, second: string | number): string | number {
if (typeof first === 'string' || typeof second === 'string') {
return `${first}${second}`;
}
return first + second;
}
// instanceof 연산자 예시
class NumObject {
count: number;
constructor(count: number) { this.count = count; }
}
function addObjects(first: object | NumObject, second: object | NumObject): number {
if (first instanceof NumObject && second instanceof NumObject) {
return first.count + second.count;
}
return 0;
}
열거형 (Enum)
열거형은 관련된 상수들의 집합을 정의할 때 사용합니다. 코드의 가독성을 높이고 오류를 줄이는 데 도움이 됩니다.
enum Status {
Pending, // 0
Processing, // 1
Completed, // 2
Failed // 3
}
function getStatusMessage(status: Status): string {
switch (status) {
case Status.Pending:
return 'Operation is pending.';
case Status.Processing:
return 'Operation is processing.';
case Status.Completed:
return 'Operation completed successfully.';
case Status.Failed:
return 'Operation failed.';
default:
return 'Unknown status.';
}
}
const currentStatus = Status.Processing;
console.log(getStatusMessage(currentStatus));
제네릭 (Generics)
제네릭은 타입스크립트에서 재사용 가능한 컴포넌트를 만들 때 사용합니다. 다양한 타입에서 작동하는 유연하고 재사용 가능한 코드를 작성할 수 있게 해줍니다.
// 제네릭 함수
function identity<T>(arg: T): T {
return arg;
}
const numResult = identity<number>(10);
const strResult = identity<string>("hello");
// 제네릭 배열
const numberArray: Array<number> = [1, 2, 3];
const stringArray: Array<string> = ["a", "b", "c"];
// 여러 개의 제네릭 타입 파라미터
function combine(first: T, second: U): string {
return `${first}${second}`;
}
combine(1, "2");
// 제네릭 타입 제약 조건
class ConstrainedContainer<T extends number | string> {
private item: T;
constructor(item: T) {
this.item = item;
}
getItem(): T {
return this.item;
}
}
const numContainer = new ConstrainedContainer<number>(100);
const strContainer = new ConstrainedContainer<string>("example");
// const boolContainer = new ConstrainedContainer<boolean>(true); // 오류 발생: boolean은 number 또는 string이 아님
TypeScript 개발 환경 설정
TypeScript 프로젝트를 설정하는 단계별 가이드입니다.
- 프로젝트 폴더를 생성하고 VSCode에서 엽니다.
- 터미널에서
npm init -y를 실행하여package.json파일을 생성합니다. tsc --init명령어로tsconfig.json파일을 생성합니다.src와build폴더를 생성하고,index.html파일을 만듭니다.tsconfig.json파일에서outDir(컴파일 결과물 출력 디렉토리)과rootDir(소스 파일 디렉토리)를 설정합니다.index.html파일에 JavaScript 파일을 포함시킵니다.src폴더에 TypeScript 파일(예:page.ts)을 작성합니다.- 터미널에서
tsc명령어를 실행하여 TypeScript 파일을 JavaScript로 컴파일합니다. index.html파일을 브라우저에서 열고 개발자 도구(F12)를 통해 출력 결과를 확인합니다.
네임스페이스 (Namespace)
네임스페이스는 코드를 논리적으로 그룹화하고 전역 스코프 오염을 방지하는 데 사용됩니다. 모듈화된 개발을 위한 기본적인 구문을 제공합니다.
namespace MyModule {
export class MyClass {
// ... class implementation
}
export const myVariable: string = "Hello";
}
const instance = new MyModule.MyClass();
console.log(MyModule.myVariable);
Parcel을 이용한 TypeScript 번들링
Parcel은 설정이 간편한 모던 웹 애플리케이션 번들러로, TypeScript 프로젝트를 쉽게 빌드하는 데 사용할 수 있습니다.
yarn add --dev parcel@next