여러 타입 중 하나일 수 있는 값을 나타낸다. (or)
'|' 연산자를 사용하여 정의할 수 있다.
함수의 인수를 처리하거나 다양한 타입의 값을 반환할 수 있는 함수를 만드는 데 용이하다.
할당할 수 있는 값을 여러가지로 확장할 수 있기 때문에 코드의 유연성이 좋아지고,
어떠한 값들을 할당할 수 있는지 명시적으로 지정하기에 예측가능한 함수를 만드는데 효과적이다.
function myFunction(input: string | number){
...
}
위의 예시는 함수의 인자로 string 혹은 number가 할당될 수 있음을 명시한다.
유니온 타입을 사용할 때, "어떠한 값이 전달되었는지" 에 대한 후처리를 해주어야 함에 주의.
예를들어,
function myFunction(input: string|number) {
return input * 2; //산술 연산의 왼쪽은 'any', 'number', 'bigint' 또는 열거형 형식이어야 합니다.ts(2362)
}
위와 같이 유니온 타입을 지정하면 string 타입은 산술 연산을 진행할 수 없다는 에러를 출력한다.
이런 경우, Type Guard 를 설정하여 전달받은 값에 대한 후처리를 해주어야 한다.
function myFunction(input: string|number):string|number{
if(typeof input === "string"){
return input + input
} else {
return input * 2
}
}
특정 코드 블록 내에서 변수의 타입을 좁히거나 확인하는 방법.
보통 조건문, typeof 연산자, instanceof 연산자를 사용해 구현한다.
타입 가드를 사용하면 특정 코드블럭 내에서 변수의 타입을 구체적으로 구분하여
타입에 따른 다른 동작 등을 정의할 수 있다.
위의 예시에서 설명한 유니온 타입의 후처리도 이와 같은 맥락이라고 볼 수 있다.
여러 타입을 하나로 결합하는 방법. (and)
'&' 연산자를 사용하여 정의할 수 있다.
기본적으로 두 가지 이상의 타입을 모두 만족하는 새로운 타입을 생성하는 역할을 한다.
기존의 타입들을 재사용한다는 측면에서 재사용성과 확장성에 이점을 가지고 있지만,
과도하게 사용했을 때 코드를 이해하기 어려워지며
아래와 같은 문제점을 가지고 있다.
type A = {
A: number;
B: string;
}
type B = {
B: number;
C: string;
}
type C = A | B
type D = A & B
const obj1: C = {
A:10,
B:"hi",
C:'hi'
}
const obj2: D ={
A:10,
B:123,//'number' 형식은 'never' 형식에 할당할 수 없습니다.ts(2322)
C:"hi"
}
type D 는 type A와 type B 의 intersection 타입이다.
intersection 타입은 "교차되는 조건에 대한 참" 이어야 하므로
위의 B 속성의 경우 "string 타입임과 동시에 number 타입" 이어야 한다.
그런 이상한 값은 이 세상에 존재하지 않으므로 typescript 자체에서 never 타입이라고
오류를 반환하는 모습.
따라서, intersection 타입에서는 교차되는 타입이 같은 경우에만 사용할 수 있다.
아래의 예시를 보면 조금 더 명확하게 잘못된 점을 찾아볼 수 있다.
type invalidType = string & boolean & number;
// 문자이면서 불리언이면서 숫자인 어떤 타입 ?
반대로 type C 에서는 B 속성에 number, string 어느 타입을 할당해도 오류를 반환하지 않는다.
typescript 는 javascript 의 class 를 확장하여 타입의 안정성을 제공한다.
기본적인 구조는 다음과 같다.
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
class Person {
public name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const person = new Person("John", 30);
console.log(person.age); // 오류: 'age' 속성은 private이므로 'Person' 클래스 외부에서 접근할 수 없습니다.
class Person {
readonly name: string;
constructor(name: string) {
this.name = name;
}
}
const person = new Person("John");
person.name = "Mike"; // 오류: 'name'은 읽기 전용 속성입니다.
class MyClass {
static myStaticProperty = "some value";
static myStaticMethod() {
return MyClass.myStaticProperty;
}
}
const myInstance = new MyClass();
console.log(myInstance.myStaticProperty); // 오류: 'myStaticProperty' 속성은 클래스 'MyClass'의 인스턴스에 존재하지 않습니다.
class Person {
constructor(public name: string) {}
}
class Employee extends Person {
constructor(public name: string, public employeeId: number) {
super(name);
}
}
abstract class Shape {
abstract area(): number;
}
const shape = new Shape(); // 오류: 추상 클래스의 인스턴스를 만들
"재사용이 가능한 코드를 만들기 위한 타입의 추상화"
마치 타입을 파라미터 처럼 사용할 수 있다.
이러한 이유 때문에 typescript 에서 제네릭을 가장 많이 사용하는 대표적인 예시는
array 와 promise 이다.
배열의 경우, 어떤 타입의 요소를 담을 수 있는지 표현할 수 있으며
프로미스의 경우, 비동기 작업이 성공했을 때 어떤 타입의 결과를 반환할 지 표현할 수 있다.
기초적인 사용법은 다음과 같다.
function identity<T>(value: T): T {
return value;
}
let number = identity<number>(123);
let string = identity<string>('abc');
let error = identity<number>('abc'); // 'abc' 타입은 number에 할당할 수 없습니다.