| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
- 책으론 원리만
- 백준
- 끝까지 잘 마무리하기
- Next.js
- javascript30
- 바닐라JS
- 실무는 공식문서로
- 공부할 거 넘많다~
- api 라우트
- git flow start
- fetch pull 차이
- js
- 차이점
- axios
- 클라이언트 컴포넌트
- TS
- 힘들었던 한주
- JavaScript
- freecodecamp
- jQuery
- Main
- 서버 컴포넌트
- Mac
- 다시 홧팅
- HTML
- CSS
- git
- AJAX
- git flow finish
- 개발일지
- Today
- Total
다다의 개발일지 6v6
[TS] JavaScript + type 체크 = TypeScript 에 대해 알아보자. 본문
JavaScript + 타입 체크 = TypeScript
js 안에 특수한 ts라고 생각하면 됨. ts ⊂ js
따라서 ts에서는 js가 돌아가지만 js에서는 ts가 돌아가지 않는다고 볼 수 있다.
TypeScript는 변수의 데이터 타입을 명확하게 지정해주어 안정성을 높여준다. → 이게 Ts의 제일 큰 특징
코드 연습 간단하게 할 수 있는 사이트
https://www.typescriptlang.org/play/?#code/Q
TS Playground - An online editor for exploring TypeScript and JavaScript
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
www.typescriptlang.org
js의 문제점
let myName = 'dada';
myName = 1; // 타입이 바뀌어 버림. 나중에 버그가 생길 수 있음.
ts의 개선방안
(오류를 발생시켜 코딩할 때 버그를 잡을 수 있도록 도와줌)
let myName: string = 'dada';
myName = 1; // 컴파일 에러
데이터 타입
기본 데이터 타입
- number: 숫자 타입으로, 정수와 실수를 포함.
- string: 문자열 타입.
- boolean: 참(true)과 거짓(false)을 나타내는 불리언 타입.
- null: 값이 없음을 나타내는 타입.
- undefined: 값이 할당되지 않은 변수의 기본값인 타입.
객체 타입
- object: 객체를 나타내는 타입. { }
const user: {name: string, age: number, greet(): void} = { name: "John", age: 25, greet() { // 메서드 정의할 때는 : function을 빼야함. console.log(`Hello, my name is ${this.name}`); } }; user.greet(); // Hello, my name is John - array: 동일한 타입의 요소를 가진 가변적인 길이의 배열을 나타내는 타입. [ ]
// 첫번째 방법 :타입 [] let arr1: number[] = [1, 2, 3]; // 두번쨰 방법 :Array<타입> let arr2: Array<number> = [1, 2, 3]; - tuple: 각 요소가 다른 타입을 가질 수 있는 고정된 길이의 배열을 나타내는 타입. [ ] (TS 전용)
let tuple: [string, number, boolean] = ["Hello", 42, true]; // 길이가 3이고 각각 string, number, boolean 타입으로 고정된 배열
특수 타입
1. any: 어떠한 타입이든 할당될 수 있는 타입입니다. (TS 전용)
- 용도: any는 모든 타입을 허용하므로, 타입 검사를 무시하고 자유롭게 값을 할당할 수 있다. any를 사용하면 타입 안정성이 없어져 가능한 오류를 감지할 수 없으므로, 최소한으로 사용하는 것이 좋다.
- 사용 예시: 타입을 알 수 없거나 임시로 타입 검사를 피하고 싶을 때 사용.
let value: any = 5;
value = "Hello"; // 타입 검사 없이 문자열로 재할당 가능
value = true; // 타입 검사 없이 boolean으로 재할당 가능
function logValue(input: any) {
console.log(input); // 어떤 타입이든 받을 수 있음
}
logValue(42);
logValue("Test");
2. unknown: 타입을 미리 알 수 없는 경우에 사용되는 타입입니다. 이 타입은 안전한 타입 검사를 위해 사용됩니다. any보다 안전한 타입입니다. (TS 전용)
- 용도: unknown은 타입을 미리 알 수 없을 때 사용하며, any와 달리 안전한 타입 검사를 강제합니다. unknown으로 선언된 변수에 대해 특정 타입의 작업을 수행하려면 타입 확인을 거쳐야 합니다.
- 사용 예시: 외부에서 제공되는 데이터나, 여러 타입을 허용하는 값에 타입 검사를 강제하고 싶을 때 사용.
let input: unknown;
input = "Hello World";
input = 42;
// 직접 사용하려면 타입 검사가 필요합니다.
if (typeof input === "string") {
console.log(input.toUpperCase()); // 타입이 string일 때만 메서드를 사용
}
function processValue(value: unknown) {
if (typeof value === "number") {
console.log(value + 10); // 타입이 number일 때만 덧셈 가능
}
}
- any vs unknown: any는 모든 타입 연산을 허용하는 반면, unknown은 타입 확인을 요구하여 보다 안전합니다.
3. never: 절대 반환 되지 않는 타입을 나타냅니다. 예를 들어, 함수가 항상 예외를 발생시키거나 무한 루프를 실행할 때 이 타입을 사용할 수 있습니다. (TS 전용)
- 용도: never 타입은 아무 값도 반환하지 않는 함수에서 사용됩니다. 예를 들어, 항상 예외를 발생시키거나 무한 루프에 빠지는 함수는 never 타입을 반환합니다.
- 사용 예시: 특정 함수가 정상적으로 종료되지 않을 때나, 발생하지 않아야 할 상황을 처리할 때 유용합니다.
// 항상 예외를 발생시키는 함수
function throwError(message: string): never {
throw new Error(message); // 함수가 정상적으로 반환되지 않음
}
// 조건에서 절대 발생하지 않는 경우를 표현할 때
function checkType(value: string | number) {
if (typeof value === "string") {
console.log("It's a string");
} else if (typeof value === "number") {
console.log("It's a number");
} else {
const impossible: never = value; // value가 string이나 number가 아니면 오류 발생
}
}
- never와 void의 차이: void는 함수가 아무 값도 반환하지 않을 때 사용하는 반면, never는 반환되지 않음을 보장합니다.
타입 추론 기능
타입스크립트는 타입 추론 기능을 가지고 있어서 변수의 타입을 자동으로 판단할 수 있다. 만약 명시적으로 타입을 지정하지 않아도 컴파일러가 초기 할당값을 기준으로 변수의 타입을 추론한다.
→ 나중에 다른 타입을 할당하려고 하면 명시적으로 타입 설정해줬을 때처럼 오류 발생
let age = 27; // age는 number로 추론해서 자동으로 타입 설정해줌.
age = 'dada' // 컴파일 error
이 기능 덕분에 코드가 더 간결해지고, 필요한 경우에만 타입을 명시할 수 있다.
모호하거나 복잡한 로직에서는 타입을 꼭 명시해주는 것이 좋다.
타입을 전부 설정하는게 나은지, 특수한 경우에만 설정하는게 나은지?
1. 일반적으로는 타입을 명시하는 것이 좋다.
- 가독성: 타입을 명시하면 다른 개발자가 코드의 의도를 더 쉽게 이해할 수 있다.
- 코드의 명확성: TypeScript가 추론하는 것보다 더 정확하고 상세한 타입 정보를 제공할 수 있다. 특히 함수의 매개변수나 반환값은 타입을 명시하는 것이 좋다.
- 유지보수: 명시적인 타입 설정은 코드가 커지거나 팀 프로젝트에서 협업할 때 오류를 줄여준다.
function add(a: number, b: number): number {
return a + b;
}
2. 타입 추론이 명확한 경우, 타입 명시를 생략해도 괜찮다.
- 간결성: 타입 추론이 명확한 경우, 타입을 생략하면 코드가 더 짧아지고 읽기 쉬워진다.
- 초기화가 명확한 변수: 변수에 기본값을 바로 할당하는 경우, 타입을 추론하도록 둬도 괜찮다.
let count = 10; // number로 자동 추론
const message = "Hello"; // string으로 자동 추론
3. 타입을 명시하는 것이 좋은 경우
- 복잡한 객체나 배열: 복잡한 객체 구조나 배열의 경우, 타입 추론이 불분명할 수 있다. 이때는 인터페이스나 타입 별칭을 사용해 명시하는 것이 좋다.
- any 사용 방지: 타입이 불분명한 경우 any로 추론될 수 있는데, 이를 방지하려면 명시적으로 타입을 지정하는 것이 좋다.
interface User {
id: number;
name: string;
age?: number;
}
const user: User = { id: 1, name: "Alice" }; // 명시적으로 User 타입 사용
4. 타입 명시가 중요한 경우
- API 데이터: 외부 API에서 받는 데이터나 동적으로 생성되는 객체는 타입이 불명확할 수 있으므로, 이럴 때는 명시적인 타입을 지정해 오류를 줄일 수 있다.
- 함수 반환 타입: 함수의 반환 타입은 TypeScript가 잘못 추론할 수 있으므로, 명시적으로 설정하는 것이 좋다.
코드가 길어질수록 명시적인 타입 설정이 유지 보수와 협업에 큰 도움이 되므로, 기본적으로 타입을 설정하는 습관을 가지는 것이 좋다!
함수의 데이터 타입
기본 형태
// (매개변수): 반환값
function add(a: number, b: number): number {
return a + b;
}
선택적 매개변수 사용하기 (?: 있어도 없어도 되는 매개변수)
function greet(name: string, **greeting?: string)**: string {
if (greeting) { // 선택적이니까 있으면 여기
return `${greeting}, ${name}!`;
} else { // 없어도 오류 아님.
return `Hello, ${name}!`;
}
}
함수의 시그니처 정의: 함수 타입을 변수로 정의할 수 있음.
type GreetFunction = (name: string) => string;
let greet: GreetFunction = (name) => `Hello, ${name}`;
interface (인터페이스)
기본 구조와 사용법
interface Person {
name: string;
age: number;
}
const person1: Person = { name: "Alice", age: 25 }; // 정상
const person2: Person = { name: "Bob" }; // 오류: 'age' 속성이 누락됨
선택적 속성
interface Car {
brand: string;
model: string;
year?: number; // 선택적 속성
}
const car1: Car = { brand: "Toyota", model: "Camry" }; // 정상
const car2: Car = { brand: "Honda", model: "Civic", year: 2020 }; // 정상
읽기 전용 속성 (Read-Only 변경 불가)
interface User {
readonly id: number;
name: string;
}
const user1: User = { id: 1, name: "Alice" };
user1.name = "Bob"; // 정상
user1.id = 2; // 오류: 'id'는 읽기 전용 속성입니다.
함수 타입 정의
interface Greeting {
(name: string): string; // 함수 시그니처
}
const greet: Greeting = (name) => `Hello, ${name}!`;
Type Aliases (타입 별칭)
보통 맨 첫글자를 대문자로 씀.
원시 데이터 타입 별칭
type Age = number;
const myAge: Age = 30;
다양한 사례
// Array
type Names = string[];
const myFriends: Names = ['Alice', 'Bob', 'Charlie'];
// Tuple
type Coordinates = [number, number];
const myLocation: Coordinates = [37.7749, -122.4194];
// 객체
type User = {
id: string;
name: string;
age: number;
};
const user: User = { id: '1', name: 'John Doe', age: 28 };
// 함수
type GreetingFunction = (name: string) => string;
const greet: GreetingFunction = (name) => `Hello, ${name}!`;
조금 더 복잡한 형태
type UserID = string;
type UserName = string;
type Age = number;
type User = {
id: UserID;
name: UserName;
age: Age;
};
const user: User = { id: '1', name: 'John Doe', age: 28 };
type(확장 불가, 유니언타입)과 interface(확장 가능, 클래스)의 차이
interface와 type은 TypeScript에서 모두 타입을 정의하는데 사용되지만, 각기 다른 목적과 특징이 있다. 두 가지 모두 객체의 구조를 정의하거나, 함수 타입을 지정하는 데 유용하지만, 몇 가지 차이점이 있다.
주요 차이점: 확장성 (확장과 병합)
interface는 확장이 가능하다.
- 다른 interface를 상속받거나, 같은 이름의 인터페이스를 여러 번 정의할 수 있으며, TypeScript는 자동으로 병합
- 이를 통해 여러 번 정의된 속성들이 하나의 인터페이스로 합쳐질 수 있다.
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
const myDog: Dog = { name: "Buddy", breed: "Labrador" };
- 인터페이스 병합:
interface Person {
name: string;
}
interface Person {
age: number;
}
const person: Person = { name: "Alice", age: 30 }; // 두 인터페이스가 병합됨
type은 확장이 제한적이다.
- 타입은 상속이나 병합을 지원하지 않는다. 대신 유니언 타입 등을 사용하여 조합할 수 있지만, interface만큼 유연하게 상속되지 않음.
- 타입 별칭을 조합할 때는 유니언(|)이나 교차(&) 연산자를 사용해야 함.
- 유니언(Union)과 교차(Intersection) 타입
- 유니언 타입: 하나 이상의 타입을 허용할 수 있다.
let value: string | number; value = "hello"; value = 42; - 교차 타입: 두 개 이상의 타입을 합쳐서 새로운 타입을 만든다.
type Person = { name: string }; type Contact = { phone: string }; type Employee = Person & Contact;
- 유니언 타입: 하나 이상의 타입을 허용할 수 있다.
type Animal = {
name: string;
};
type Dog = Animal & {
breed: string;
};
const myDog: Dog = { name: "Buddy", breed: "Labrador" };
| interface | type | |
| 확장성 | 다른 인터페이스 상속 및 병합 가능 | 상속 불가, 유니언 타입과 교차 타입으로 조합 |
| 사용 범위 | 주로 객체 타입 정의 | 유니언, 튜플, 객체, 함수 등 다양한 타입 정의 가능 |
| 유니언 및 교차 타입 | 지원하지 않음 | 지원함 |
| 클래스 구현 | 클래스에서 구현 가능 | 클래스 구현에 직접 사용하지 않음 |
| 가독성과 코드 스타일 | 객체 구조 정의 시 가독성 높음 | 복잡한 타입 표현 시 유연하고 간결함 |
제네릭 (Generics)
- 제네릭은 다양한 타입을 처리하는 함수나 클래스를 만들 때 사용.
- 타입을 함수나 클래스가 호출되는 시점에 지정할 수 있도록 하여, 코드의 재사용성과 타입 안전성을 높인다.
- 예를 들어 배열의 모든 항목을 출력하는 함수를 만들 때 number 배열, string 배열 모두 처리할 수 있도록 제네릭을 사용할 수 있다.
function identity<T>(arg: T): T {
return arg;
}
const output = identity<string>("Hello"); // T가 string으로 설정됨