// 1. type[ ]
let nums: number[] = [1, 2, 3, 4, 5] // 숫자만 허용
let color: string[] = ["red", "green", "blue"] // 문자열만 허용
nums.push(7) // 가능 : 숫자 타입만 허용
nums.push("8") // 불가능 : 숫자 타입에 문자열 타입을 넣을 수 없다는 오류 발생
nums[0] = "1" // 불가능 : nums 의 0번째 인덱스에 문자 타입을 할당할 수 없다는 오류 발생
// 2. Array<type>
let str: Array<string> = ["foo", "bar", "baz"]
let flags: Array<boolean> = [false, false, false]
falags.push(1) // 불가능 : 불리언 타입만 허용 가능
[ 객체 타입을 통해 ]
• 객체의 형태/구조를 보장하고 예측
• 예상치 못한 값이 들어가는 것을 방지
[ 객체의 Property 타입 ]
• optional (옵셔널): 특정 속성이 필수가 아닌 선택적임을 표현, 속성이름 뒤에 물음표 (“?”) 를 사용
• readonly (읽기전용): 객체의 속성이 수정되지 않아야할때 사용. 속성 이름 앞에 “readonly” 키워드를 추가하여 사용
[ 타입 별칭 (Type Alias) ]
• type 키워드를 사용하여 타입 별칭을 생성한다
• 객체의 구조를 정의하고, 정의된 구조에 이름(별칭)을부여하는 방법
// 기본 객체 타입
// {
// property_name: property_type
// }
let car: { make: string; model: string, year: number }
car = {
make: "Volkswagen",
model: "Tiguan",
year: 2020
}
// 📍 [타입 명시]
let monitor: { brand: string; price: number }
// CASE: 1
// monitor.brand = "LG"
// monitor-price = 120
// CASE: 2
monitor = {
brand: "LG",
price: 120
}
// 오류 : 모니터 객체에 지정한 타입 내에 존재하지 않음
monitor.displaySize = "22inch"
// 오류 : price 프로퍼티를 설정하지 않음
monitor = {
brand: "LG"
}
// 📍 [타입 추론]
// 객체를 생성하면서 임의적인 타입 구조가 완성됨 이로 타입을 추론함
// monitor1 위에 마우스 호버 시 brand 는 문자 타입으로, price 는 숫자 타입으로 자동 추론
let monitor1 = {
brand: "LG",
price: 120
}
// 오류 : price 에는 숫자 타입만 할당 가능
monitor1.price = "123"
// 오류 : 모니터 객체에 지정한 타입 내에 존재하지 않음 오류 발생
monitor1.displaySize = "22inch"
// 📍 [Optional 프로퍼티] - 선택사항
let user: { id: string; name: string; age?: number };
// age 값은 optional 프로퍼티 (?) 설정으로 선택사항임. 넣지 않아도 오류 발생하지 않음
user = {
id: "1234",
name: "John"
}
// 📍 [Readonly 프로퍼티]
let user1: { readonly id: string; name: string; age?: number };
user1 = {
id: "1234",
name: "John"
}
// 정상
user1.name = "John Smith";
// 오류 : Readonly 를 사용하여 처음 할당 된 1234 라는 값으로 고정됨
// Cannot assign to 'id' because it is a read-only property.
user1.id = "5678"
// + Readonly 프로퍼티 예제 중 자주 쓰이는 문법
let apiConfig: {
readonly clientKey: string;
readonly url: string;
}
// 📍 [타입 별칭 (Type Alias)]
// 하나의 객체 타입을 만들어서 변수처럼 여러번 사용 가능
type UserType = {
id: string;
name: string;
age: number;
}
let user11: UserType = {
id: "1",
name: "John",
age: 20
}
// 오류 가능성 : 처음 할당한 age의 값이 없거나, 아예 없는 값인 height의 값을 넣게 되면 오류 발생
let user22: UserType = {
id: "2",
name: "Sarah",
age: 20,
// height : 160
};
let users: UserType[] = [];
// 정상 : UserType 의 객체 형태만 원하기 때문에 가능
users.push(user11);
users.push(user22);
// 오류 : UserType의 형태가 아님
users.push({}) ;
// 오류 : UserType의 형태가 아니며, 모든 프로퍼티가 들어가지 않았음
users.push({
id: "1"
});
// 📍 Nested 객체 ( 중첩 객체 )
type Payload = {
timestamp: number;
type: string;
user: {
readonly id: string;
isActive?: boolean;
email: string[];
}
}
const payload: Payload = {
timestamp: 12893718,
type: 'event',
user: {
id: "123",
isActive: true,
email: ["qwer12@gmail.com", "qwer112@gmail.com"]
}
}
function add(x: number, y: number): number {
return x + y;
}
function sayHi(name: string, greeting: string = "Hi"): string {
return `${greeting}, ${name}`;
}
Parameter 타입 : 함수에 전달하는 매개 변수의 데이터타입을 지정하며, 올바른 개수가 전달되는지 또한 확인한다Optional Parameter 타입 : 함수에 필수가 아닌 선택적 매개변수를 지정할 수 있다Contextual 타입 : 함수(콜백 함수)의 맥락에 따라 자동으로 타입을 추론하는 기능Return 타입 : 함수가 반환하는 값의 데이터 타입을 명시void 반환 타입 : 아무것도 반환하지 않는 함수never 반환 타입 : 절대 반환하지 않는 함수
// 📍 Parameter
function add(x: number, y: number): number {
return x + y;
}
// 반환 타입도 number 이므로 result 도 자동적으로 number 타입으로 명시됨
const result = add (10, 5);
// 📍 Parameter II
function addToCart(name: string, price: number, quantity: number) {
return `${name}, ${price}, ${quantity}`;
}
// 정상
addToCart("orange", 100, 5)
// 오류 : Expected 3 arguments, but got 2.
addToCart("pineapple", 150)
// 오류 : Expected 3 arguments, but got 4.
addToCart ("pineapple", 150, 5, false)
// 📍 Defalt Parameter
// quantity = number 타입으로 1이라는 파라미터가 기본 설정 -> 생략 가능
function addToCart1(name: string, price: number, quantity: number = 1) {
return `${name}, ${price}, ${quantity}`;
}
addToCart1("orange", 10) // "orange, 10, 1" 반환
addToCart1("pineapple", 5, 3) // "pineapple, 5, 3" 반환
// 📍 optional Parameter
function addToCart2(name: string, price: number, quantity?: number ) {
return `${name}, ${price}, ${quantity}`;
}
addToCart2("orange", 150) // "orange, 150" 반환
addToCart2("pineapple", 100, 2) // "pineapple, 100, 2" 반환
// + 추가 optional Parameter
// 아래 코드로 작성 시 quantity의 경우 선택이지만, 따로 지정하지 않을 경우 1로 설정
function addToCart3(name: string, price: number, quantity?: number ) {
return `${name}, ${price}, ${quantity || 1}`;
}
// 📍 Contextual Typing
// 타입을 명시하지 않았는데도, 타입스크립트가 주변 문맥을 보고 타입을 추론하는 것
const nums = [1, 2, 3];
const doubled = nums.map(num => num * 2);
// 여기서 num은 타입을 명시하지 않았지만 타입스크립트는 nums가 number[]임을 알고 있어서 num의 타입을 자동으로 number로 추론함
// 📍 Return Type annotation (반환 타입 명시)
// 함수가 예상치 못한 타입의 리턴값을 방지하기 위함
function addTwoValues(x: number, y: number): string {
return `${x}${y}`;
}
function addTwoNumbers(x: number, y: number): number {
return x + y;
}
function isTen(x: number, y: number): boolean {
return x + y === 10;
}
// 📍 Void: 해당 함수가 반환 값이 없다. / never: 해당함수는 절대로 반환 값이 존재하지 않음을 명시
// console.log()처럼 뭔가 실행되긴 하지만 값을 반환하지 않는 함수에 사용
// 흔히 말하는 "부수 효과(Side Effect)" 함수들
function logMessage(message:string): void {
console. log (message)
}
// 이 함수는 실행 도중 예외를 던지고 종료됨
// 따라서 "아무것도 반환할 수 없다" → never
function throwError (message: string): never {
throw new Error(message)
}
// userId 의 타입은 string 또는 number 가 될 수 있다.
let userId: string | number;
userId = 1
userId = "1"
// id 파라미터의 타입은 string 또는 number 가 될 수 있다
function printUserId(id: string | number) {
console. log(userId)
}
// 모두 가능
printUserId("1")
printUserId(1)
// 📍 type narrowing
// 유니언 타입의 구체적인 하위타입을 좁혀나가는 방식 (“타입 좁히기” 라고도 표현한다)
// 변수의 타입이 여러 가지일 수 있을 때, 조건문 등을 통해 구체적인 타입으로 좁히는 것
function processValue(value: number | string) {
// 문자열 경우
if(typeof value === 'string') {
return value.toUpperCase; // string으로 좁혀짐
}
// 숫자의 경우
return value.toString().toUpperCase(); // number로 좁혀짐
}
// 📍 배열 사용 예시
// 서로 다른 타입의 요소를 포함할 수 있는 배열을 정의하여 유연성을 증가시킨다
let mixedValues: (string | number)[] = [];
mixedValues.push("100")
mixedValues.push(100)
// 오류 : []는 빈 배열, 즉 타입스크립트 입장에서 보면 never[] 또는 any[] 같은 “배열 타입”
mixedValues.push([])
// 📍 리터럴과 유니언 타입
// 제한된 값을 가진 타입(예: 특정 문자열이나 숫자의 집합) 을 표현
const toggle = (option: "on" | "off") => {
console. log(option);
}
toggle ("on")
toggle ("off")
// 오류 : "on" | "off" 외에 불가
toggle (true)
toggle (0)
type Size = "xs" | "s" | "m" | "1" | "xl";
let tShirtSize: Size;
tShirtSize = "m"
// 오류 : "xs" | "s" | "m" | "1" | "xl" 여기서 선언해준 값만 사용 가능하다
tShirtSize = "xxl"
// 아래 코드처럼 사용 가능
function setSize(size: Size) {
switch(size) {
case "xs" :
// Xs 처리
break;
case "s" :
// s 처리
break;
//..
}
}
// 📍 type aliases와 유니언
// 타입 또는 리터럴값을 결합하여 재사용 가능한 타입을 정의
type SuccessCode = 200 | 201 | 202
type ErrorCode = 500 | 501 | 503
let responseCode: SuccessCode | ErrorCode
responseCode = 200
responseCode = 503
// 오류 : Type '403' is not assignable to type 'SuccessCode | ErrorCode'.
responseCode = 403
interface Person {
name: string;
age: number;
}
interface Student extends Person {
studentNumber: number;
}
// 확장 사용
const student: Student = {
name: "Amy",
age: 26,
studentNumber: 12345,
}
두 기능의 목적은 크게 다르지 않다. 크게 확장 문법이 다르다
type alias
interface
💡 [ 객체 타입 정의 ]
// type alias
type Person = {
name: string;
age: number;
};
// interface
interface Person {
name: string;
age: number;
}
💡 [ 확장 (상속) ]
// interface 확장
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// type 확장 (교차 타입 사용)
type Animal2 = {
name: string;
};
type Dog2 = Animal2 & {
breed: string;
};
💡 [ 유니언 타입 (type만 가능) ]
type Status = "loading" | "success" | "error"; // ✅ type만 가능
// interface Status = "loading" | "success" ❌ 불가능
💡 [ 함수 타입 ]
// type
type Greet = (name: string) => string;
// interface
interface GreetFn {
(name: string): string;
}
// 📍 readonly & optional property
interface User {
readonly id: number;
name: string;
isPremium?: boolean;
}
const testUser: User = {
id: 100,
name: "John"
}
// 오류 : readonly 이기 때문에 할당 불가
testUser. id = 200
// 📍 메서드/함수 타입
interface User1 {
readonly id: number;
name: string;
isPremium?: boolean;
greet(message: string): string;
}
const testUser1: User1 = {
id: 100,
name: "Nathan",
isPremium: false,
greet(message: string) {
return `${message}, ${this.name}`;
},
}
testUser1.greet("Hello"); // Hello, Nathan
// 📍 확장 및 조합
// 두 개 이상의 인터페이스 조합도 가능하다
interface Person {
name: string;
age: number;
}
interface Employee {
EmployeeId : number;
}
interface Student extends Person, Employee {
studentNumber: number;
}
// 확장 사용
const student: Student = {
name: "Amy",
age: 26,
studentNumber: 12345,
EmployeeId: 45678
}
let myTuple: [string, number, boolean];
myTuple = ["Hello", 42, true]; // OK
myTuple = ["Hello", false, 42]; // Error : number, boolean 순서 바뀜
myTuple = ["Hello", 42]; // Error : boolean 값 명시 X
// 📍 [1]
function getUserInfo(): [number, string] {
return [1, "Sam"]
}
// 자동으로 userid 는 number 타입, username은 string 타입 으로 지정된다
const [userid, username] = getUserInfo();
// 📍 [2]
type Flavor = string;
type Price = number;
type IceCream = [Flavor, Price]
const vanila: IceCream = ["Vanila", 500]
// 자동으로 0번째는 string , 1번째는 number 타입이다.
vanila[0]
vanila[1]
// 📍 [3]
type lat = number;
type lng = number;
type coord = [lat, lng];
let coords: coord[] = [];
coords. push([36, -95]);
coords. push ([38, -76]);
coords.push([false, "1"]);
// 타입 안정성 보장
for (const [lat, lng] of coords) {
console. log(lat, lng);
}
// enum 안에 있는 상수 값들을 enum의 멤버 라고 한다.
// 그리고 멤버는 각자마다 할당값이 존재한다
// 아예 명시하지 않으면 상수값1 = 0 부터 1씩 증가 하는 값으로 지정되며
// 상수값 1 = 1 으로만 지정했다면 다음은 2, 3, --
// 상수값 2= 15 로만 지정했다면 다음은 16, 17 --
enum 이름 {
상수값 1,
상수값 2
}
// 📍 [숫자형 Enum]
enum PlayerState {
Buffering, // 0
Playing, // 1
Paused, // 2
Seeking, // 3
}
// enum에 있는 값들 중 하나를 갖게 할 때 또는 비교할 때 사용
let currentState: PlayerState;
// currentState = PlayerState.Buffering;
currentState = PlayerState.Playing;
// 오류 : 문자열로 바로 넣는 건 불가능
currentState = "Playing"
const isPlaying = (state: PlayerState) => {
return state === PlayerState.Playing;
}
// 플레이어가 재생중인지 확인
isPlaying (currentState) ; // true : PlayerState.Playing 값을 명시 함
// 📍 [문자형 Enum]
enum Direction {
Left = "LEFT",
Right = "RIGHT",
Up = "UP",
Down = "DOWN"
}
function move(dir: Direction) {
switch(dir) {
case Direction.Left:
// 왼쪽 로직
break;
case Direction. Right:
// 오른쪽 로직
break;
// ...
}
}
move (Direction. Left);
move(Direction.Right);
https://stackblitz.com/edit/vitejs-vite-9myckcey?file=index.ts