유니언 타입은 두개 이상의 다른 타입을 조합해 만든 타입이다. 유니언 타입을 만들기 위해 조합한 타입들을 각각 유니언 타입의 멤버라고 한다.
function combine(input1: string | number, input2: string | number) {
  let result;
  if (typeof input1 == "number" && typeof input2 == "number") {
    // 유니언 타입을 이용해 함수를 구현한 후 런타임 타입 체크를 통해 argument들의 type의 종류에 따라 유연하게 기능을 구현할 수 있다.
    result = input1 + input2;
  } else {
    result = input1.toString() + input2.toString();
  }
  return result;
}
const combineNumber = combine(1, 44);
console.log(combineNumber); // => 45
const conbineName = combine("변", "진상");
console.log(conbineName); // => 변진상
리터럴 타입은 단순히 특정 변수나 매개변수가 아니다. 정확한 값을 가지는 타입이다. 일반적인 number, string, object... 등과 같은 타입 이외에도, 구체적인 문자열과 숫자 값을 타입 위치에서 지정할 수 있다.

위 사진과 같이 cnstNumb라는 변수는 타입할당을 하지 않았는데, const 키워드로 선언한 값이 변하지 않는 number값이다. 그래서 TS에서는 이를 2.8이라는 리터럴이 타입으로 지정된 것을 볼 수 있다.
리터럴 타입 자체는 별 의미가 없을 수 있으나, 유니언과 같이 쓰인다면 효과적으로 사용될 수 있다.
 만약 함수 선언 당시 리터럴 타입으로 주어진 "string"이나 "number"타입이 아닌 argument가 주어질 경우 경고를 띄운다.
만약 함수 선언 당시 리터럴 타입으로 주어진 "string"이나 "number"타입이 아닌 argument가 주어질 경우 경고를 띄운다.
Boolean 타입도 리터럴타입이다. Boolean 리터럴 타입의 경우 true, false 단 두개의 타입만이 존재한다. Boolean 타입은 true | false 유니언타입의 별칭(alias)이다.
Type alias를 사용하면 타입의 별칭을 줄 수 있다. 다음과 같이 일반적인 core type의 별칭을 줄수 있다.
type NumberNumber = number;그러나 단순히 한가지의 타입에 별칭을 지어 사용할 경우 코드를 쓰고 읽어감에 큰 혼란을 줄 수 있기 때문이 지양한다.
type alias는 특히 union type과 literal type의 조합에서 빛을 발한다.
function combineFunc(
  input1: string | number,
  input2: string | number,
  to: "toStr" | "toNumb"
) {
  if (to === "toStr") {
    return input1.toString() + input2.toString();
  } else {
    return +input1 + +input2;
  }
}위 코드의 경우 union type으로 string | number가 반복되고 있다. 단 두 번만 쓰일 경우는 별 무리가 없어보이지만 만약 1억번 사용한다면 의미가 있을 것이다.
type Combinable = string | number;
type ToTo = "toStr" | "toNumb";
function combineFunc(input1: Combinable, input2: Combinable, to: ToTo) {
  if (to === "toStr") {
    return input1.toString() + input2.toString();
  } else {
    return +input1 + +input2;
  }
}이렇게 코드를 간결하게 정리 할 수 있을 것이다.
전개구문을 이용해 응용할 수 있다.
type Role = "teacher" | "student" | "parents" | "dog";
const jack: { name: string; role: [Role, ...Role[]] } = {
  name: "Jack",
  role: [
    "teacher",
    "student",
    "parents",
    "parents",
    "parents",
    "parents",
    "dog",
  ],
};
console.log(jack);

타입 별칭을 이용해 객체타입에도 별칭을 붙일 수 있다.
/* -------------------------------------------------------------------------- */
/*                          Type Alias를 이용한 객체 타입 간소화                    */
/* -------------------------------------------------------------------------- */
type User = { name: string; age: number };
function getUserStatus(userObj: User) {
  return console.log(`name: ${userObj.name}, age: ${userObj.age}`);
}
const jack: User = {
  name: "잭",
  age: 1111,
};
console.log(getUserStatus(jack)); 이렇게도 사용 가능...
이렇게도 사용 가능...
function add(num1: number, num2: number): number {
  return num1 + num2;
}
// number라고 명시할 수 있지만 TS가 add 함수가 number를 리턴함을 추론할 수 있기 때문에 생략하는 것도 좋다.
function printResult(num: number): void {
  console.log(num);
}
// 이 경우 undefined를 return하는데, void는 함수가 undefined나 아무것도 return하지 않음을 명시한다.
function printResult2(num: number): undefined {
  console.log(num);
  return;
}
// 이렇게 undefined를 리턴함을 명시할 수 있지만 함수 내에 return;을 작성해줘야 경고를 띄우지 않으며, 이렇게는 거의 사용하지 않는다.아래 코드와 같이 다를 함수를 지시하는 변수(나는 이해를 위해 함수를 가리키는 pointer라고 했다.)나 Callback에서 사용할 수 있는 Function Type이 있다.
/* -------------------------------------------------------------------------- */
/*                              자료형으로서의 Function                           */
/* -------------------------------------------------------------------------- */
let funcPointer: Function;
funcPointer = add;
funcPointer = printResult;
이렇게 funcPointer가 Funtion 타입의 데이터를 할당할 것임을 명시했기 때문에, 기존에 작성한 add, printResult 둘 다 할당이 가능하다.
하지만 이를 좀 더 엄격하게 할당하기 위해 함수의 명세(parameter의 자료형, return type)를 자료형으로 명시할 수 있다.
/* -------------------------------------------------------------------------- */
/*                         더욱 엄격한 자료형 제공을 위한 함수 명세 이용                 */
/* -------------------------------------------------------------------------- */
let strictFuncPointer: (a: number, b: number) => number;
strictFuncPointer = add;
// strictFuncPointer = printResult; 
// !!! ERROR: 자료형으로 제공한 함수의 명세와 일치하지 않기 때문에 Error !!! 이렇게 에러를 띄운다!
이렇게 에러를 띄운다!
call back 함수의 type의 반환값이 void로 없다고 명시하더라도, 실제로는 callback 함수에서 데이터를 반환할 수 있다.
/* -------------------------------------------------------------------------- */
/*                   함수의 인자로 주어지는 Callback function의 자료형 명시            */
/* -------------------------------------------------------------------------- */
function addAndHandle(
  num1: number,
  num2: number,
  cb: (result: number) => void
) {
  const addedNum = num1 + num2;
  console.log(cb(addedNum)); // 콜백 함수의 반환값이 void라도 값을 반환할 수 있다.
}
const isReturn = addAndHandle(11, 22, (result) => {
  console.log(result);
  return true;
});
let anyVar: any;
let unknownVar: unknown;
anyVar = 3;
console.log(anyVar.toUpperCase()); // !!! 런타임 ERROR: 컴파일 당시에는 문제가 없다. !!!
console.log(typeof anyVar);
unknownVar = 2;
//console.log(unknownVar.toUpperCase());
// !!! ERROR: 컴파일 전에 unknown 자료형이기 때문에 타입체킹을 더 엄격히 하라고 에러를 띄운다.!!!
if (typeof unknownVar === "string") {
  console.log(unknownVar.toUpperCase());
}
// 이렇게 타입체킹을 거치면 에러를 띄우지 않는다.
console.log(typeof unknownVar);any: 모든 타입의 값이 할당될 수 있다. TS에서 타입체킹을 유연하게 하기 때문에 컴파일 후 브라우저에서 실행하는 런타임시 에러를 띄울 수 있다.(예를 들어 number type의 데이터가 할당된 변수에 string의 메서드를 사용한 경우)
unknown: 모든 타입의 값이 할당될 수 있다. TS에서 타입체킹을 컴파일러가 엄격하게 하기 때문에, 위 코드와 같이 타입체킹을 위한 추가적인 코드를 요구하기도 한다.
never type 에러나 무한루프 등고 같이 코드가 중단 됨에 따라 이후의 코드에 도달할 수 없어 절대 값을 리턴할 수 없는 경우에 사용한다. void와 같지만 never라고 표기함으로써 sementic meaning을 추가해 코드 품질의 관점에서 의도를 더 분명히 할 수 있다.
/* -------------------------------------------------------------------------- */
/*                                 never type                                 */
/* -------------------------------------------------------------------------- */
function genError(message: string, code: number): never {
  throw { message: message, errorCode: code };
  //무한루프: while(true){}
}
genError("an error occurred!", 500);