[[Classes & Interfaces]] 참조
type Admin = {
name: string;
privileges: string[];
};
type Employee = {
name: string;
startDate: Date;
};
type ElevatedEmployee = Admin & Employee;
const e1: ElevatedEmployee = {
name: 'Max',
privialeges: ['create-server'],
startDate: new Date()
};
interface의 extends와 동일하게 작동함
interface Admin {
name: string;
privileges: string[];
}
interface Employee {
name: string;
startDate: Date;
}
interface ElevatedEmployee extends Admin, Employee {}
const e1: ElevatedEmployee = {
name: 'Max',
privialeges: ['create-server'],
startDate: new Date()
};
유니온 타입에서 제공하는 유연성으로 인해 런타임에 타입이 어떤 유형인지 알 수 있어야하기 때문에 사용
typeof
type Combinable = string | number;
function add(a: Combinable, b: Combinable) {
if (typeof a === 'string' || typeof b === 'string') {
return a.toString() + b.toString();
}
return a + b;
}
### in
```typescript
type Admin = {
name: string;
privileges: string[];
};
type Employee = {
name: string;
startDate: Date;
};
type UnknownEmployee = Employee | Admin;
function printEmployeeInformation(emp: UnknownEmployee) {
console.log('Name: ' + emp.name); // 문제 없음
console.log('Privileges: ' + emp.privileges); // ERROR
}
위와 같이 Employee 타입인지 Admin타입인지 알 수 없기에 에러가 뜸
function printEmployeeInformation(emp: UnknownEmployee) {
console.log('Name: ' + emp.name);
if (typeof === 'Admin') { // ERROR
console.log('Privileges: ' + emp.privileges);
}
}
typeof 와 비교하는 것은 JavaScript 타입과만 비교할 수 있음 (number, string, object etc) 따라서 'Admin'타입은 TypeScript만 알고있기 때문에 안 됨
따라서 'in' typeguard를 사용하는 방법이 있음
function printEmployeeInformation(emp: UnknownEmployee) {
console.log('Name: ' + emp.name);
if ('privileges' in emp) {
console.log('Privileges: ' + emp.privileges);
}
if ('startDate' in emp) {
console.log('Start Date: ' + emp.startDate);
}
}
class에서도 in typeguard를 쓸 수 있음.
class Car {
drive() {
console.log('Driving...');
}
}
class Truck {
drive() {
console.log('Driving a truck...');
}
loadCargo(amount: number) {
console.log('Loading cargo...');
}
}
type Vehicle = Car | Truck;
const v1 = new Car();
const v2 = new Truck();
function useVehicle(vehicle: Vehicle) {
vehicle.drive();
if (vehicle instanceof Truck) {
vehicle.loadCargo(1000);
}
}
useVehicle(v1); // Driving...
useVehicle(v2); //Driving a truck..., Loading cargo...
instanceof 는 Built in JavaScript이기 때문에 class에서 사용이 가능하다.
그러나, interface는 TypeScript에서 추가된 것이기 때문에 사용이 불가능.
따라서 if (vehicle instanceof Truck)에서 Truck은 class이므로 사용이 가능함.
(JavaScript에도 class(prototype기반)가 있기 때문, 자세한 부분은 tsc로 컴파일)
interface Bird {
flyingSpeed: number;
}
interface Horse {
runningSpeed: number;
}
type Animal = Bird | Horse;
function moveAnimal(animal: Animal) {
if ('flyingSpeed' in animal) {
console.log('Speed is: ' + animal.flyingSpeed);
}
if ('runningSpeed' in animal) {
console.log('Speed is: ' + animal.runningSpeed);
}
}
위와 같이 할 수 있지만 코드 복잡성과 오타가 생길경우 수정에 복잡성이 생길 수 있다.
interface Bird {
type = 'bird';
flyingSpeed: number;
}
interface Horse {
type = 'horse';
runningSpeed: number;
}
type Animal = Bird | Horse;
function moveAnimal(animal: Animal) {
let speed;
switch (animal.type) {
case 'bird':
speed = animal.flyingSpeed;
break;
case 'horse':
speed = animal.runningSpeed;
}
console.log('Speed is: ' + speed);
}
moveAnimal({type: 'bird', runningSpeed: 10}); // ERROR
moveAnimal({type: 'bird', flyingSpeed: 10}); // Speed is: 10
이렇게 interface에 리터럴 타입을 부여하고 코드를 짜게 되면 함수 호출시 오류부분과 오타 발생시 코드의 수정에서도 이득을 갖고올 수 있다.
const userInput = document.getElementById('user-input');
userInput.value = 'Hi there!'; // ERROR
TypeScript는 HTML코드를 알지 못하기 때문에 에러가 나게 됨.
따라서 HTMLInputElement라는 것을 알려줘야 함.
// no.1
const userInput = <HTMLInputElement>document.getElementById('user-input')!;
// no.2
const userInput = document.getElementById('user-input')! as HTMLInputElement;
둘과 동일한 작업을 하지만 React에서 혼동을 줄 수 있기 때문에 as 사용을 권장
const userInput = document.getElementById('user-input')! as HTMLInputElement;
userInput.value = 'Hi there!';
또, 코드 뒤 !는 앞의 코드가 null이 아님을 개발자가 명확히 아는 경우 사용할 수 있음.
아래와 같이도 사용 가능하기도 함.
const userInput = document.getElementById('user-input');
if (userInput) {
(userInput as HTMLInputElement).value = 'Hi there!';
}
유연한 객체 생성을 위함
interface ErrorContainer {
// id: string; // 문제 없음
// id: number; // ERROR, []: string과 타입이 같아야 함.
[prop: string]: string;
}
const errorBag: ErrorContainer = {
email: 'Not a valid email!',
2: 'hi'; // 2는 문자열로 인식 됨
};
// 사용 예시
let prop = 'email';
console.log(errorBag[prop]); // "Not a valid email!"
type Combinable = string | number;
function add(a: Combinable, b: Combinable) {
if (typeof a === 'string' || typeof b === 'string') {
return a.toString() + b.toString();
}
return a + b;
}
const result = add('Max', 'Schwarz');
result.split(' '); // ERROR, type이 Combinable로 되어있기 때문에 number일 수도 있어서
type Combinable = string | number;
function add(a: Combinable, b: Combinable) {
if (typeof a === 'string' || typeof b === 'string') {
return a.toString() + b.toString();
}
return a + b;
}
const result = add('Max', 'Schwarz') as string;
result.split(' ');
as를 사용하여 해결할 수 있지만 올바른 대안이 아닐 수 있음
type Combinable = string | number;
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: string, b: number): string;
function add(a: number, b: string): string;
function add(a: Combinable, b: Combinable) {
if (typeof a === 'string' || typeof b === 'string') {
return a.toString() + b.toString();
}
return a + b;
}
const result = add('Max', 'Schwarz');
result.split(' ');
위와 같이 함수 오버로드를 사용해서 입력 값에 따라 반환값을 결정해주는 방법으로 해결 가능
const fetchedData = {
name : 'park',
}
console.log(fetchedData.age); // ERROR
console.log(fetchedData?.age); // 작동 X
data?.age 하게되면 ?의 왼쪽 데이터 즉, data가 null or undifined면 작동 안 하게 됨
에러를 사전에 방지할 수 있음, 하지만 에러를 해결한 것은 아님
const data = {
name : 'park',
}
console.log(data.age ?? 'Hi'); // Hi
?? 왼쪽 데이터가 null or undifined면 오른쪽 실행
예외 처리에 매우 유용