TypeScriptSTUDY _ 2장 _.타입 [ 2.2 타입스크립트의 타입 시스템 ]

zeroha·2024년 11월 18일
0

TypeScriptStudy

목록 보기
3/32
post-thumbnail

2.2 타입스크립트의 타입 시스템

.
.
.

1. 타입 애너테이션 방식

타입 애너테이션 type annotation : 변수나 상수 혹은 함수의 인자와 반환 값에 타입을 명시적으로 선언 -> 어떤 타입 값이 저장될 것인지를 컴파일러에직접 알려주는 문법 ( 언어마다 타입 명시 방법 다름 )

비교 >
자바 : 변수에 데이터 타입을 명시 x -> 식별자를 찾지 못했다고 에러남.
항상 변수 이름보다 데이터 타입을 우선 명시해줘야 함.

test = "test";
// error : cannot find symbol test

타입스크립트 : 변수 이름 뒤에 : type 구문 붙여 <- 데이터 타입 명시
type선언부를 제거해줘도 코드가 정상적으로 작동하긴 함. 타입 추론 과정이 어려워질 뿐.

let isDone : boolean = false;

2. 구조적 타이핑

타입 사용 프로그래밍 언어 <- 값이나 객체는 하나의 구체적인 타입을 가지고 있음.

타입시스템 : 타입은 이름으로 구분되며 컴파일러 이후에도 남아있음. 이것을 명몽적으로 구체화한 것.

명확한 상속관계나 공통 인터페이스x 경우 : 타입 호환 x

interface Developer {
    faceValue : number;
}

interface BankNote {
    faceValue : number;
}

let developer : Developer = { faceValue : 52 };
let bankNote : BankNote = { faceValue : 1000 };

developer = bankNote; // ok
bankNote = developer; // ok

타입스크립트 : 구조로 타입을 구분( = 구조적 타이핑 )


3. 구조적 서브타이핑

타입스크립의 타입 = 값의 집합
so... 특정 값이 string 또는 number 타입 동시에 가질 수 있음.

type stringOrNumber = string | number;

이처럼 집합으로 나타낼 수 있는 타입스크립트의 타입 시스템을 지탱하고 있는 개념이 "구조적 서브타이핑"

구조적 서브타이핑 : 객체가 가지고 있는 속성(프로퍼티) 바탕으로 타입을 구분하는 것, 이름이 다른 객체라도 속성이 동일하면 서로 호환 가능

interface Pet {
    name : string
}

interface Cat {
    name : string
    age : number
}

let pet : Pet;
let cat : Cat = { name : "Zag", age : 2}

pet = cat

: Cat은 Pet과 다른 타입으로 선언... but Pet이 갖고 있는 name이라는 속성 가짐.
so... Cat 타입으로 선언한 cat을 Pet 타입으로 선언한 pet에 할당가능.
구조적 서브 타이핑 -> 함수의 매개변수에도 적용 가능

interface Pet {
    name : string
}

let cat = { name : "Zag", age : 2}

function greet (pet: Pet) {
    console.log("Hello," + pet.name)
}

: greet( ) 함수의 매개변수에 들어갈 수 있는 값은 Pet 타입으로 제한.
...but 타입 명시 x Cat객체를 전달해도 정상 작동 o
왜냐면 : cat 객체는 Pet 인터페이스가 가지고 있는 name 속성을 가지고 있어 pet.name 속성에 접근 가능해서

so... 타입의 상속 역시 구조적 타이핑 기반 o

class Person {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}

class Developer {
    name: string;
    age: number;
    sleepTime: number;

    constructor(name: string, age: number, sleepTime: number) {
        this.name = name;
        this.age = age;
        this.sleepTime = sleepTime;
    }
}

function greet(p: Person) {
    console.log(`Hello, I'm ${p.name}`);
}

const developer = new Developer("zig", 20, 7);
greet(developer); // Hello, I'm zig

: Developer 클래스가 Person 클래스를 상속받지 않았는데도 greet(developer)는 정상작동
왜냐 : Developer는 Person이 갖고있는 속성을 가지고 있기 때문

  • 서로 다른 두 타입 간의 호환성 : 오로지 내부의 구조에 의해 결정 o
    so... 타입이 계층 구조로부터 자유롭다.

4. 자바스크립트를 닮은 타입스크립트

타입스크립트의 타입 시스템 = 구조적 서브타이핑
<-> 명목적 타이핑( ex. C++, 자바 ) : 같은 이름의 데이터 타입으로 선언된 경우에만 서로 호환됨.


class Cat {
  String name;
  public void hit () {}
}

class Arrow{
  String name;
  public void hit () {}
}

public class Main {
  public static void main (String[] args) {
   // error: incompatible types: Cat cannot be converted to Arrow
  Arrow cat = new Cat();
   // error: incompatible types: Arrow cannot be converted to Cat
  Cat arrow = new Arrow();
  }
}

: Cat과 Arrow 클래스는 String 타입의 name 변수와 hit() 메서드를 가지고 있다는 점에서 구조적으로 동일, 각 클래스로 생성한 인스턴스는 서로 호환 x
so... 구조가 같더라도 이름이 다르다면 다른 타입으로 취급
왜냐면? 명목적 타이핑을 채택한 언어에서는 이름으로 타입을 구분해서.

명목적 타이핑 : 타입의 동일성 equivalence을 확인하는 과정에서 구조적 타이핑에 비해 조금 더 안전함.
so... 객체의 속성을 다른 객체의 속성과 호환되지 않도록 안전성 추구함.

그런데도 타입스크립트가 구조적 타이핑을 채택한 이유? 타입스크립트가 자바스크립트를 모델링 -> 자바스크립트는 본질적으로 덕 타이핑 기반

덕타이핑 : 어떤 함수의 매개변숫값이 올바르게 주어진다면 그 값이 어떻게 만들어졌는지 신경쓰지 않고 사용한다는 개념, 어떤 타입에 부합하는 변수와 메서드를 가질 경우 해당 타입에 속하는 것으로 간주.

타입스크립트는
: 이런 동작을 그대로 모델링, 자바스크립트의 특징을 그대로 받아들여 명시적인 이름을 가지고 타입을 구분하는 대신 객체나 함수가 가진 구조적 특징을 기반을 타이핑하는 방식을 채택 (쉬운 사용성, 안정성, 객체 간 속성이 동일하다면 서로 호환)
: 객체가 가진 속성을 기반으로 타입을 검사

타입을 검사하는 시점에 따라_

  • 덕타이핑
    : 런타임에 타입을 검사
  • 구조적 타이핑
    : 컴파일타임에 타입을 검사
    [ 공통점 : 객체 변수, 메서드 같은 필드를 기반으로 타입을 검사 ]

5. 구조적 타이핑의 결과


interface Cube {
    width: number;
    height: number;
    depth: number;
}

function addLines(c: Cube) {
    let total = 0;
    for (const axis of Object.keys(c)) {
        // Element implicitly has an 'any' type
        // because expression of type 'string'
        // can't be used to index type 'Cube'.
        // No index signature with a parameter of type 'string' was found on type 'Cube'.
        
     const length = c[axis];

     total += length;
    }
}

: addLines () 함수의 매개변수인 c는 Cube 타입으로 선언, Cube 인터페이스의 모든 필드는 number 타입 -> c[axis]는 당연히 number 타입이라 예측
...but c에 들어올 객체 : 어떤 속성이든 가질 수 있음.
so... c[axis]의 타입이 string일 수도 있어 에러 발생


const namedCube {
    width: 6,
    height: 5,
    depth: 4,
    name: "test" // string 타입의 추가 속성이 정의
};

addLines(namedCube); // ok

: 타입스크립트-> c[axis]가 어떤 속성인지 알 수 x number라고 확정 x => error 발생 ( Cube타입 값이 들어갈 곳에 name 같은 추가 속성을 가진 객체도 할당할 수 있기 때문에 발생 )
이러한 한계 극복 : 유니온 (: 타입스크립트에 명목적 타이핑 언어의 특징을 가미, 식별)


6. 타입스크립트의 점진적 타입 확인

점진적 타입 검사 : 컴파일 타임에 타입을 검사하면 필요에 따라 타입 선언 생략을 허용

타입 선언이 생략 -> 동적 검사 수행 : 암시적 타입 변환이 일어남.


function add(x,y){
	return x+y;
}

// 위 코드는 아래와 같이 암시적 타입 변환이 일어난다.
function add(x: any, y:any):any;

add()함수의 매개변수 x와 y에 타입 선언 x
...but 타입스크립트 컴파일러 : 모두 any 타입으로 추론

필요에 따라 타입 생략 o, 타입을 점진적으로 추가할 수도 o
-> 반드시 모든 타입을 알고 있어야 x
( 알고 있을 때 최상의 결과 o )

타입 지정 x 자바스크립트 코드 -(마이그레이션)-> 타입스크립트
: 타입스크립트의 점진적 타이핑이라는 특징 유용하게 활용 o

타입스크립트의 타입 시스템은 정적 타입의 정확성 100% 보장 x

any 타입 : 타입스크립트 내 모든 타입의 종류 포함 가장 상위 타입, 어떤 타입 값이든 할당 가능.
... but, noImplicityAny 값 = true 일 때 에러 발생
왜냐면? noImplicityAny는 타입 애너테이션이 없을 때 변수가 any 타입으로 추론되는 것을 허락 x
타입스크립트 코드 작성시, 정확한 타이핑을 위해 tsconfig의 noImplicityAny 값 = true 로 설정하는 게 좋음.


7. 자바스크립트 슈퍼셋으로서의 타입스크립트

타입스크립트 = 기존 자바스크립트 + 정적인 타이핑 추가
(TS = JS모든 문법 포함 / 선택적으로 타이핑 도입 -> 진입장벽 낮음.)

모든 JS = TS
모든 TS != JS


function  greet(name:string){
	console.log("Hello",name);
}

: 타입스크립트에서는 유효 o / 자바스크립트에서는 오류


let developer = 'Colin';
console.log(developer.toUppercase());

: 이경우, 에러 메세지 차이_
타입스크립트 : // Did you mean 'toUpperCase'?
-> 타입을 추론해서 메서드 대체를 제안
자바스크립트 : // developer .toIppercase is not a function
-> 그냥 이렇게 에러를 던져줌.


8. 값 vs 타입

값 value : 프로그램이 처리하기 위해 메모리에 저장하는 모든 데이터 = 프로그램에서 조작하고 다룰 수 있는 어떤 표현 (다양한 형태의 데이터를 포함)


11; // 숫자 값
"hello typescript"; // 문자열 값
let foo = "bar"// 변숫값

: 객체 역시 값이다. 자바스크립트에서는 함수도 값이다. 모든 것이 객체인 언어답게 자바스크립트 함수는 런타임에 객체로 변환됨.


//함수
function goWork(developer){
	console.log(`tired ${developer}`)
}

: 값은 어떠한 식을 연산한 것으로 변수에 할당 = developer라는 변수에 "zig"라는 문자열 값을 할당.


type Person = {
    name : string;
    age : number;
};

interface Person {
    name : string;
    age : number;
}

: type이나 interface 키워드로 커스텀 타입을 정의
값 공간과 타입 공간의 이름은 서로 충돌 x
-> 같은 이름 정의 가능 o
-> type 선언 내용 <- 자바스크립트 런타임에서 제거


type Developer = { isWorking : true };
const Developer = { isTyping : true }; //ok

type Cat = { name : string; age : number; };
const Cat = { slide : true };

: 타입은 주로 타입선언(:) 또는 단언문(as)으로 작성
값은 할당연산자인 =으로 작성


interface Developer {
    name : string;
    isWorking : boolean;
}

const developer : Developer = { name : "Zig", isWorking : true };

: 변수 developer의 타입 : Developer이며,
developer 할당값 : { name : "Zig", isWorking : true }


function test(author:Developer, date:Date): Response {
    //...
}

: author의 값으로 Developer 타입이 사용

  • 타입스크립트
    : 값과 타입이 함께 사용
    : 개발자가 작성한 코드문맥 스스로 값, 타입 해석 (위치에 따라 추론)

function email(options:{person:Person; subject:string;}){
    //..
}

: 자바스크립트의 구조분해할당 사용 -> email 함수의 매개변수로 넘기는 options 객체를


function email({person, subject, body}){
    //..
}

이렇게 풀어 쓸 수 있다.

.. but, 타입스크립크트를 구조 분해 할당하면 오류가 발생함.


function email({person:
	Person, // subject;
    string, // body;
    string,
    //..
}

: 값의 관점에서 Person가 string이 해석되어서 오류가 남.
개발자 의도 : person:Person
코드해석 : Person이 값공간에 있는걸로 해석

  • 값-타입 공간을 혼동하는 문제 해결? 값-타입 공간 구분해서 작성하기

구조분해할당 : 자바스크립트 또는 타입스크립트에서 배열이나 객체의 속성을 개별 변수로 분해하여 그 값을 변수에 할당하는 것


class Rectangle {
  constructor (height, width) {
    this.height = height;
    this.width = width;
  }
 }

const rect1 = new Rectangle(5, 4);
  • 값과 타입 공간에 동시 존재 심볼 : class & enum
  • class
    : 객체 인스턴스를 더욱 쉽게 생성하기 위한 문법 기능
    : 실제 동작은 함수와 같음
    : 동시에 클래스는 타입으로도 사용됨.
    so... 타입스크립트 코드에서 클래스는 값과 타입 공간 모두에 포함

class Developer {
  name: string;
  domain: string;

constructor(name: string, domain: string) {
	this.name = name;
	this.domain = domain;
   }
 }

const me: Developer = new Developer ("zig", "frontend");

: 변수명 me 뒤에 등장하는 : Developer에서 Developer는 타입에 해당, new 키워드 뒤의 Developer는 클래스의 생성자 함수인 값으로 동작.

타입스크립트에서 클래스는 타입 애너테이션으로 사용
런타입에서 객체로 변환 -> 자바스크립트의 값으로 사용되는 특징.

  • enum 역시 런타임에 객체로 변환되는 값
    : enum은 런타임에 실제 객체로 존재하며, 함수로 표현 가능.

enum Direcion{
 Up, // 0
 Down, //1
}

: 클래스처럼 타입 공간에서 타입을 제한하는 역할 ... but 자바스크립트 런타임에서 실제 값으로도 사용 가능


enum WeekDays {
  MON = "Mon"
  TUES = "Tues",
  WEDNES = "Wednes",
  THURS = "Thurs",
  FRI = "Fri",
}

// 'MON' " "TUES' I "WEDNES' I "THURS' I FRI
type WeekDaysKey = keyof typeof WeekDays;
function printDay(key: WeekDaysKey, message: string) {
  const day = WeekDays [key];
  if (day < WeekDays.WEDNES) {
  console.log(`It's still ${day)day, $(message}`);
  }
}
 
 printDay ("TUES", "wanna go home");

: enum이 타입으로 사용됨.
WeekDays enum에 keyof typeof 연산자룔 사용 -> type WeekDaysKey만들어 printDay() 함수의 key 인자에 넘겨줄 수 있는 값의 타입을 제한

이와 달리,


enum Mycolors {
  BLUE = "#0000FF",
  YELLOW = "#FFF00",
}


function whatBlueColor(pallette : {BLUE:string}) {
  return pallette.BLUE;
}
 
 whatBlueColor (Mycolors);

MyColors enum은 마치 일반적인 객체처럼 동작
whatBlueColor () 함수의 인자인 palette는 BLUE라는 속성을 갖는 객체, MyColors는 string 타입의 BLUE속성을 가지고 있기 때문에 코드가 정상 실행됨.

  • 타입스크립트파일을 자바스크립트 파일로 변환해도 여전히 자바스크립트 하일에 해당 정보가 남아있음... but, 타입으로만 사용되는 요소는 컴파일 이후에 자바스크립트 파일에서해당 정보가 사라진다.

9. 타입을 확인하는 방법

  • typeof
    : 연산하기 전에 피연산자의 데이터 타입을 나타내는 문자열 반환
    (자바스크립트의 7가지 기본 데이터 타입, Function, 호스트객체, object)

typeof 2022; // "number" 
typeof "woowahan"; // "string" 
typeof true; // "boolean" 
typeof 0; // "object

: 타입스크립틍 = 값& 타입 공간 별도로 존재
typeof 연산자도 값에서 쓰일 때와 타입에서 쓰일 대 역할이 다름.


interface Person {
  first: string; 
  last: string;
}

const person: Person = { first: "zig", last: "song" };

function email(options: person: Person; subject: string; body: string })

: 값에서 사용된 typeof는 자바스크립트 런타임의 typeof 연산자가 된다.


const v1 = typeof person; // 값은 object;
const v2 = typeof email; // 값은 'function'

: 반면 타입에서 사용된 typeof는 값을 일고 타입스크립트 타입을 반환


type T1 = typeof person; // 타입은 Person
type T2 = typeof email; // 타입은 options: { person: Person: subject: str
string; }) => void

person 변수가 interface Person 타입으로 선언, 타입공간에서의 typeof person은 Person을 반환

  • person변수가 interface Person 타입으로 선언 -> 타입 공간에서의 typeof person은 Person을 반환
    email 함수 : 함수의 매개변수타입과 리턴 타입을 포함한 함수 시그니터 타입을 반환.

  • v1, v2 : const 키워드로 선언된 변수로 값이 할당될 공간,
    값 공간의 typeof 는 피연산자인 person과 email의 런타임 타입으르 가리키는 문자열을 반환.

  • 값에서 사용된 typeof 연산자는 자바스크립트 typeof 연산자와 동일하게 동작


도서 참조 : 우아한 타입스크립트 with 리액트

profile
하 영

0개의 댓글

관련 채용 정보