[TS] 7. Typescript's Type System
by jbee
Table of Contents
TypeScript의Type Checking System
Type Inference
Type Assertion
Type Guards
Type Compatibility
TypeScript의Typing Checking System
 TypeScript 에서의Type System에대한이해를하기 전, 기존프로그래밍
언어의큰두축인정적언어와동적언어에대한정의를다시한번살펴볼필
요가 있습니다.
정적언어(Static Language)
변수(variables) 또는함수(function)의 Type 을미리지정해야한다.
컴파일되는시점에Type Check를수행한다.
동적언어(Dynamic Language)
변수(variables) 또는함수(function)의 Type 을지정하지않는다.
Type Check는런타임(runtime) 환경에서나알수있다.
Duck Typing
덕타이핑(Duck Typing)이라고 많이들어보셨을텐데요, 현재이덕타이핑
체계를기반으로동적언어에타입을추론하는언어는GoLang과 Python 등
이있습니다. 하지만TypeScript는이덕타이핑과는조금 다른체계로
Typing을하고 있습니다.
덕타이핑에대한보다자세한내용은여기를참고해주세요.
Structural typing
TypeScript는Structural typing (구조적타이핑)을기반으로타입시스템
을갖추고 있습니다. 구조적타이핑이란,  멤버 에따라타입을연관짓는방법
을말합니다. 구조적타이핑과 반대인방법으로 nominal typing 이있습니
다. 우리가 알고 있는일반적인정적언어인C#, Java는이 nominal
typing 방식으로type checking이이루어집니다.
_Official Document_에나온예제를통해설명드립니다.
interface Named {
name: string;
}
class Person {
name: string;
}
let p: Named;
p = new Person(); // Ok, because of structural typing
위코드를C# 또는Java의문법에맞게 변경한다면동작하지않는잘못된코
드가 됩니다. 하지만TypeScript에서는정상적으로동작합니다.  Named 와
 Person 두가지는오로지 name 이라는프로퍼티(or 멤버)만갖고 있는타
입이므로서로 compatibility 하다고 볼수있습니다. 때문에위코드는문
제되지않습니다. (compatibility에대해서는뒤에서알아봅니다.)
Type Inference
'누가봐도이변수는이타입이다.'라는것에대해TypeScript가 지원해주는
것을타입추론이라고 합니다.
let name = `jbee`;
 name 이라는변수는 string 타입의변수가 됩니다. 그렇기 때문에굳이 :
string 이라고 타입을지정해주지않아도다음과 같이에러가 발생합니다.
name = 1; // Error: Type '1' is not assignable to type 'string'
그렇다면다음과 같은경우에는어떻게 추론될까요?
const mixedArr = [0, 1, `jbee`];
 number 에해당되는value와 string 에해당하는value가 공존하기 때문
에위코드에서 mixedArr 은 (number | string)[] 의타입을갖게 됩니
다. 이렇게 여러타입이공존하는경우에추론하여지정되는타입을Best
common type이라고 합니다.
Type Assertion
이변수의타입은분명 A 인데TypeScript에서보수적으로처리하여에러를
발생시키는경우가 있습니다. 이럴경우해당변수를 A 라고 명시하여에러를
사라지게 할수있습니다.
export type Todo = {
id: number;
text: string;
completed: boolean;
};
export type Todos = Todo[];
위와같이 Todo 타입과  Todos 타입을지정한상황이라고 했을때를예로
들어보겠습니다.
const initialState: Todos = [
{
id: 0,
text: 'Study RxJS',
completed: false,
}
];
위코드에서 Todos 에해당하는타입을제대로지정해줬지만뭔가 아쉬움이
남을수있는데요, 이때두가지방법을사용할수있습니다.
const initialState: Todos = [
<TODO>{
id: 0,
text: 'Study RxJS',
completed: false,
}
];
위코드처럼 <> 을사용하거나
const initialState: Todos = [
{
id: 0,
text: 'Study RxJS',
completed: false,
} as Todo
];
위코드처럼 as 키워드를사용할수있습니다. 두가지모두동일하지만
 tsx 와함께사용하기 위해서는 as 키워드를사용하는것이좋습니다.
[!] 이Type Assertion은Type Casting과는다릅니다. 자세한내용은
DailyEngineering ‑ 타입추론과 타입단언을참고해주세요!
Type Guards
자바스크립트에서는 typeof 또는 instanceof 와같은오퍼레이터가 타
입을확인해주는역할을했었습니다.
if (typeof this.state[key] !== typeof newData) {
return ;
}
하지만이방법은런타임에서타입체크를수행하게 됩니다. 따라서컴파일시
점에서는올바른타입인지알수없습니다. TypeScript에서는컴파일시점에
서타입체크를수행할수있도록 Type Guard 를지원합니다.
 typeof ,  instanceof 
TypeScript에서도마찬가지로 typeof 와 instanceof 오퍼레이터를지
원합니다.
function setNumberOrString(x: number | string) {
if (typeof x === 'string') {
console.log(x.subtr(1)); // Error
console.log(x.substr(1)); // OK
} else {
console.log(typof x); // number
}
x.substr(1); // Error
}
위코드에서 if-block 내에서의 x 변수의타입은 string 일수밖에없다
는것을컴파일시점에체크하여transpiler가 Error를발생시키는경우입니다.
이예제와비슷한방법으로 instanceof 오퍼레이터를사용할수있습니다.
 instanceof 는클래스를기반으로생성된인스턴스의타입을판단하는데
사용됩니다.
class Pet {
name = 123;
common = '123';
}
class Basket {
size = 123;
common = '123';
}
function create(arg: Pet | Basket) {
if (arg instanceof Pet) {
console.log(arg.name); // OK
console.log(arg.size); // Error!
}
if (arg instanceof Basket) {
console.log(arg.name); // Error!
console.log(arg.size); // OK
}
console.log(arg.common); // OK
console.log(arg.name); // Error!
console.log(arg.size); // Error!
}
 typeof 와마찬가지로 instanceof 로필터링된block 내부에서Type
checking이이루어집니다.
 in 
interface A {
x: number;
}
interface B {
y: string;
}
function execute(q: A | B) {
if ('x' in q) {
// q: A
} else {
// q: B
}
}
 A 또는 B 를유니온타입으로받을수있는 execute 함수내에서각각에
대해다른처리를할경우,  in 이라는오퍼레이터를사용할수있습니다. 해당
오퍼레이터는check하고자하는타입에해당프로퍼티가 존재하는지의유무
를판단할수있습니다.
 .kind Literal Type Guard
사용자에의해 type 으로정의된타입에대해서, 즉TypeScript 내부에서지
원하는primitive type이아닌사용자정의타입에대해서타입검사를수행할
때,  .kind 를사용할수있습니다.
type Foo = {
kind: 'foo', // Literal type
foo: number
}
type Bar = {
kind: 'bar', // Literal type
bar: number
}
function execute(arg: Foo | Bar) {
if (arg.kind === 'foo') {
console.log(arg.foo); // OK
console.log(arg.bar); // Error!
}
}
위코드에서처럼 type 키워드를사용하여별도타입을지정할때,  kind 라
는프로퍼티를추가하여타입검사를수행할수있습니다.
User Defined Type Guards
메소드를별도로분리하여Type Guard를지정할수있습니다. 아까 지정한
interface A로만들어보겠습니다.
interface A {
x: number;
}
// Define Type Guard
function isA(arg: any): arg is A {
return arg.x !== undefined;
}
 arg is A 라는타입으로 isA 메소드가 Type Guard의역할을수행한다
는것을명시할수있습니다. if 내부에들어가는로직을별도로추출하여보다
가독서이좋은코드를작성할수있습니다.
Type Compatibility
한국어로번역하게 되면타입호환성정도로할수있겠는데요, TypeScript는
위에서언급했듯이Structural subtyping을기반으로Type checking을하
기 때문에이를기반으로타입간의호환성을고려할수있습니다.
1. Comparing two Objects
let x = {name: `Jbee`};
let y = {name: `James`, age: 34};
x = y; // OK!
y = x; // Error!
위코드에서 x 는 {name: string} 타입으로추론되며,  y 는 {name:
string, age: number} 로추론됩니다. 이경우 x = y 는 y 에 name 이
라는속성이있으므로가능하지만 y = x 의경우,  x 에는 age 라는속성이
없으므로에러가 발생합니다.
2. Comparing two functions
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK
x = y; // Error
함수일경우에는객체인경우와조금 다른것을볼수있습니다. 위코드에서
두 x ,  y 함수는parameter 만다르게 정의되어있습니다. 이경우,  x 함수
에전달할수있는parameter의경우의수가 y에모두해당하므로 y = x 가
정상적으로동작합니다. 하지만그 반대인 x = y 는 y 함수에전달할수있
는parameter를 x 가 모두포용할수없으므로에러가 발생합니다.
let x = () => ({name: `Jbee`});
let y = () => ({name: `James`, age: 34});
x = y; // OK!
y = x; // Error!
이번에는return value의type이다른경우입니다. 이경우에는함수의경우
를따르지않고 객체인경우를따르게 됩니다.
마무리
TypeScript의단순한문법을조금 넘어서어떻게 Type Checking이이루어
지는지살펴봤습니다.
감사합니다.
Reference
TypeScript Official Document ‑ Type Inference
TypeScript Official Document ‑ Type Compatibility
Golang으로만나보는duck typing
Type Systems: Structural vs Nominal typing explained
TypeScript Deep dive ‑ Type Guard

07. type system

  • 1.
    [TS] 7. Typescript'sType System by jbee
  • 2.
    Table of Contents TypeScript의TypeChecking System Type Inference Type Assertion Type Guards Type Compatibility
  • 3.
    TypeScript의Typing Checking System  TypeScript 에서의TypeSystem에대한이해를하기 전, 기존프로그래밍 언어의큰두축인정적언어와동적언어에대한정의를다시한번살펴볼필 요가 있습니다.
  • 4.
  • 5.
  • 6.
    Duck Typing 덕타이핑(Duck Typing)이라고많이들어보셨을텐데요, 현재이덕타이핑 체계를기반으로동적언어에타입을추론하는언어는GoLang과 Python 등 이있습니다. 하지만TypeScript는이덕타이핑과는조금 다른체계로 Typing을하고 있습니다. 덕타이핑에대한보다자세한내용은여기를참고해주세요.
  • 7.
    Structural typing TypeScript는Structural typing(구조적타이핑)을기반으로타입시스템 을갖추고 있습니다. 구조적타이핑이란,  멤버 에따라타입을연관짓는방법 을말합니다. 구조적타이핑과 반대인방법으로 nominal typing 이있습니 다. 우리가 알고 있는일반적인정적언어인C#, Java는이 nominal typing 방식으로type checking이이루어집니다.
  • 8.
    _Official Document_에나온예제를통해설명드립니다. interface Named{ name: string; } class Person { name: string; } let p: Named; p = new Person(); // Ok, because of structural typing 위코드를C# 또는Java의문법에맞게 변경한다면동작하지않는잘못된코 드가 됩니다. 하지만TypeScript에서는정상적으로동작합니다.  Named 와  Person 두가지는오로지 name 이라는프로퍼티(or 멤버)만갖고 있는타 입이므로서로 compatibility 하다고 볼수있습니다. 때문에위코드는문 제되지않습니다. (compatibility에대해서는뒤에서알아봅니다.)
  • 9.
    Type Inference '누가봐도이변수는이타입이다.'라는것에대해TypeScript가 지원해주는 것을타입추론이라고합니다. let name = `jbee`;  name 이라는변수는 string 타입의변수가 됩니다. 그렇기 때문에굳이 : string 이라고 타입을지정해주지않아도다음과 같이에러가 발생합니다. name = 1; // Error: Type '1' is not assignable to type 'string'
  • 10.
  • 11.
    const mixedArr =[0, 1, `jbee`];  number 에해당되는value와 string 에해당하는value가 공존하기 때문 에위코드에서 mixedArr 은 (number | string)[] 의타입을갖게 됩니 다. 이렇게 여러타입이공존하는경우에추론하여지정되는타입을Best common type이라고 합니다.
  • 12.
    Type Assertion 이변수의타입은분명 A 인데TypeScript에서보수적으로처리하여에러를 발생시키는경우가 있습니다.이럴경우해당변수를 A 라고 명시하여에러를 사라지게 할수있습니다. export type Todo = { id: number; text: string; completed: boolean; }; export type Todos = Todo[]; 위와같이 Todo 타입과  Todos 타입을지정한상황이라고 했을때를예로 들어보겠습니다.
  • 13.
    const initialState: Todos= [ { id: 0, text: 'Study RxJS', completed: false, } ]; 위코드에서 Todos 에해당하는타입을제대로지정해줬지만뭔가 아쉬움이 남을수있는데요, 이때두가지방법을사용할수있습니다.
  • 14.
    const initialState: Todos= [ <TODO>{ id: 0, text: 'Study RxJS', completed: false, } ]; 위코드처럼 <> 을사용하거나
  • 15.
    const initialState: Todos= [ { id: 0, text: 'Study RxJS', completed: false, } as Todo ]; 위코드처럼 as 키워드를사용할수있습니다. 두가지모두동일하지만  tsx 와함께사용하기 위해서는 as 키워드를사용하는것이좋습니다.
  • 16.
    [!] 이Type Assertion은TypeCasting과는다릅니다. 자세한내용은 DailyEngineering ‑ 타입추론과 타입단언을참고해주세요!
  • 17.
  • 18.
    하지만이방법은런타임에서타입체크를수행하게 됩니다. 따라서컴파일시 점에서는올바른타입인지알수없습니다.TypeScript에서는컴파일시점에 서타입체크를수행할수있도록 Type Guard 를지원합니다.
  • 19.
     typeof ,  instanceof  TypeScript에서도마찬가지로 typeof 와 instanceof 오퍼레이터를지 원합니다. function setNumberOrString(x:number | string) { if (typeof x === 'string') { console.log(x.subtr(1)); // Error console.log(x.substr(1)); // OK } else { console.log(typof x); // number } x.substr(1); // Error } 위코드에서 if-block 내에서의 x 변수의타입은 string 일수밖에없다 는것을컴파일시점에체크하여transpiler가 Error를발생시키는경우입니다. 이예제와비슷한방법으로 instanceof 오퍼레이터를사용할수있습니다.  instanceof 는클래스를기반으로생성된인스턴스의타입을판단하는데 사용됩니다.
  • 20.
    class Pet { name= 123; common = '123'; } class Basket { size = 123; common = '123'; }
  • 21.
    function create(arg: Pet| Basket) { if (arg instanceof Pet) { console.log(arg.name); // OK console.log(arg.size); // Error! } if (arg instanceof Basket) { console.log(arg.name); // Error! console.log(arg.size); // OK } console.log(arg.common); // OK console.log(arg.name); // Error! console.log(arg.size); // Error! }  typeof 와마찬가지로 instanceof 로필터링된block 내부에서Type checking이이루어집니다.
  • 22.
     in  interface A { x:number; } interface B { y: string; } function execute(q: A | B) { if ('x' in q) { // q: A } else { // q: B } }  A 또는 B 를유니온타입으로받을수있는 execute 함수내에서각각에 대해다른처리를할경우,  in 이라는오퍼레이터를사용할수있습니다. 해당 오퍼레이터는check하고자하는타입에해당프로퍼티가 존재하는지의유무 를판단할수있습니다.
  • 23.
     .kind Literal Type Guard 사용자에의해 type 으로정의된타입에대해서,즉TypeScript 내부에서지 원하는primitive type이아닌사용자정의타입에대해서타입검사를수행할 때,  .kind 를사용할수있습니다.
  • 24.
    type Foo ={ kind: 'foo', // Literal type foo: number } type Bar = { kind: 'bar', // Literal type bar: number } function execute(arg: Foo | Bar) { if (arg.kind === 'foo') { console.log(arg.foo); // OK console.log(arg.bar); // Error! } } 위코드에서처럼 type 키워드를사용하여별도타입을지정할때,  kind 라 는프로퍼티를추가하여타입검사를수행할수있습니다.
  • 25.
    User Defined TypeGuards 메소드를별도로분리하여Type Guard를지정할수있습니다. 아까 지정한 interface A로만들어보겠습니다. interface A { x: number; } // Define Type Guard function isA(arg: any): arg is A { return arg.x !== undefined; }  arg is A 라는타입으로 isA 메소드가 Type Guard의역할을수행한다 는것을명시할수있습니다. if 내부에들어가는로직을별도로추출하여보다 가독서이좋은코드를작성할수있습니다.
  • 26.
    Type Compatibility 한국어로번역하게 되면타입호환성정도로할수있겠는데요,TypeScript는 위에서언급했듯이Structural subtyping을기반으로Type checking을하 기 때문에이를기반으로타입간의호환성을고려할수있습니다.
  • 27.
    1. Comparing twoObjects let x = {name: `Jbee`}; let y = {name: `James`, age: 34}; x = y; // OK! y = x; // Error! 위코드에서 x 는 {name: string} 타입으로추론되며,  y 는 {name: string, age: number} 로추론됩니다. 이경우 x = y 는 y 에 name 이 라는속성이있으므로가능하지만 y = x 의경우,  x 에는 age 라는속성이 없으므로에러가 발생합니다.
  • 28.
    2. Comparing twofunctions let x = (a: number) => 0; let y = (b: number, s: string) => 0; y = x; // OK x = y; // Error 함수일경우에는객체인경우와조금 다른것을볼수있습니다. 위코드에서 두 x ,  y 함수는parameter 만다르게 정의되어있습니다. 이경우,  x 함수 에전달할수있는parameter의경우의수가 y에모두해당하므로 y = x 가 정상적으로동작합니다. 하지만그 반대인 x = y 는 y 함수에전달할수있 는parameter를 x 가 모두포용할수없으므로에러가 발생합니다.
  • 29.
    let x =() => ({name: `Jbee`}); let y = () => ({name: `James`, age: 34}); x = y; // OK! y = x; // Error! 이번에는return value의type이다른경우입니다. 이경우에는함수의경우 를따르지않고 객체인경우를따르게 됩니다.
  • 30.
    마무리 TypeScript의단순한문법을조금 넘어서어떻게 TypeChecking이이루어 지는지살펴봤습니다. 감사합니다.
  • 31.
    Reference TypeScript Official Document‑ Type Inference TypeScript Official Document ‑ Type Compatibility Golang으로만나보는duck typing Type Systems: Structural vs Nominal typing explained TypeScript Deep dive ‑ Type Guard