Dev/JS Family, HTML, CSS
[TypeScript] 타입스크립트 입문(5) - 타입 시스템
youngst
2022. 9. 17. 03:09
타입 시스템
동적 타입과 정적 타입
타입 체커가 컴파일 타임에 수행되는지, 런타임에 수행되는지에 따라 동적 타입과 정적 타입으로 나뉜다.
정적 타입 - 컴파일 타임에 수행
- 개발자가 타입을 명시적으로 타이핑해야하는 언어
-> Java, C#, C++ 등 - 타입 체커와 타입 추론을 통해 일부 타이핑을 생략할 수 있는 언어
-> TypeScript, Scala, Haskell 등 - 컴파일 타임에 타입 검사를 수행하기 때문에 보다 빠르게 오류를 잡아낼 수 있다
-> 런타임 에러 방지 가능! - !!자동 완성!!을 통해 데이터 타입 혹은 속성에 대한 확신을 가질 수 있다!!!
동적 타입 - 런타임에 수행
- 동적 타입 시스템은 런타임 상황에도 타입이 동등한지 확인한다.
-> JavaScript, Python, Ruby, Perl, PHP 등 - JavaScript는 느슨한 타입의 동적 타입 언어
- 변수는 어떠한 타입과도 연결되지 않는다.
- 프로그램이 실행 중에 어떤 타입의 값으로든 할당하거나 재할당 할 수 있으며, 타입을 변경하거나 객체에 새로운 속성이나 메서드를 추가할 수 있다. -> 간편하지만 에러를 잡아내기 어렵고 안전한 코딩이 힘들다!
interface Person {
name: string
age: number
}
//인터페이스 덕분에 자동완성이 가능!
const obj: Person = {
name: 'N',
age: 99
}
function getPersonInfo({name, age}: Person) {
obj.age
obj.name
}
구조적 타입과 명목적 타입
타입스크립트가 타입을 이해하는 방법
구조적 타입 (Structural Type)
- 객체가 어떤 속성들을 가지는지
- 즉, 구조를 기준으로 타입을 따진다. - 구조가 같다면 타입이 같다!(주의)
- 기본적으로 자바스크립트(& 타입스크립트)는 구조적 타입의 언어이다.
interface Person {
name: string
age: number
}
interface Animal {
name: string
age: number
}
const me: Animal = {
name: 'Jang',
age: 99
}
function getName(obj: Person) {
return obj.name
}
getName(me) //error 발생 안 함 - 구조는 똑같기 때문!
getName({ name: 'hello', age: 10 }) //error 발생 안 함
명목적 타입 (Nominal Type)
- 각 타입이 고유하다는 것을 의미
- 즉, 동일한 타입이나 데이터가 있더라도 타입을 공유할 수 없다.
- 이름 기반으로 타입을 따진다.
- 작성한 타입이 런타임에 존재한다.
- C#, Java 등
덕 타이핑
- 타입스크립트의 타입은 구조적 타입
- 타입의 생김새가 오리와 같다면 그것은 오리의 타입임을 뜻한다.
-> 이름이 다르더라도 오리처럼 보이고, 오리처럼 수영하고, 오리처럼 꽥꽥거리면 그것은 오리일 것이다. - 거위가 오리와 같은 속성을 가지고 있다면 그것조차도 오리 타입!
interface Duck {
name: string
swim: Function
home: 'river'
}
// 오리와 거위는 같은 타입!
interface Goose {
name: string
swim: Function
home: 'river'
}
상위 타입과 하위 타입
- TypeScript의 모든 타입은 집합일 뿐이다.
- 때문에 타입 간에도 계층이 존재한다.
- 타입 간의 관계는 특정 관계로 선언되었는지 여부가 아닌 포함된 속성에 의해 결정된다.
- 최상위 타입은 시스템에서 허용하는 모든 가능한 값(타입)을 설명하는 타입
- 서브 타입은 슈퍼(상위) 타입에 할당할 수 있다.
-> 반대는 불가능!
상위 타입(Super Type)
- any
- unknown
하위 타입(Sub Type)
- 상위 타입과 하위 타입 사이에는 무수히 많은 타입이 존재한다.
- never
타입 캐스트
- 타입 변수를 다른 타입의 변수에 할당하며 생기는 타입 오류로, 그 계층을 확인할 수 있다.
- extend 키워드 이용
업 캐스트
- 상위 타입에 하위 타입을 할당
- 안전한 상황으로 인지하여 컴파일러 오류없이 대부분 암시적으로 가능
다운 캐스트
- 하위 타입에 상위 타입을 할당
- 안전하지 않은 상황으로 인지하여 컴파일러 오류 발생
- any는 예외
/**
* 상위 타입과 하위 타입
*/
let anyType: any
anyType = 'string'
anyType = 123
anyType = true
let unknownType: unknown
unknownType = 'string'
unknownType = 123
unknownType = true
//상위 타입 하위 타입 구분 방법
type A = Array<string> extends Object ? true : false //true
type B = Function extends Object ? true : false //true
interface Animal {
name: string
}
interface Person {
name: string
age: number
}
//Upcast, Downcate 가능 여부 판별
type PersonAndAnimal = Animal extends Person ? true : false //false
// Array와 function은 Object의 하위 타입
let obj: Object
obj = []
obj = function() { }
건전성 (Soundness)
타입스크립트의 건전한 타입 시스템 개념
- 건전함의 반대인 불건전함(Unsoundness)은 런타임에 문제를 유발할 수 있지만, 타입스크립트는 일부 불건전한 동작을 허용한다.
- 컴파일 타임에 알기 어려운 특정 타입을 안전하게 허용하는 것을 의미한다.
- 즉, 컴파일러가 런타임 시점 값의 타입을 보장할 수 있다는 개념
- 이는 모든 JavaScript 코드를 지원하기 위함!
- 건전한 언어는 데이터가 타입이 말하는 것과 일치하는지 확인하기 위해 때때로 런타임 검사를 사용
- TypeScript는 변환된 코드가 런타임에 영향을 미치지 않는 것을 목표로 한다.
/**
* 건전성 (Soundness)
*/
const x = [0, 1, 2]
//불건전하다..
x[10].toPrecision()
// any
let strOrNum:any = 999;
strOrNum = 'Hello'
//any는 불건전을 허용
strOrNum.toPrecision(9)
/**
* 타입 단언
*
* 사용자가 타입을 가장 잘 알고 있다는 것을 명시하는 방법
*/
const cardNumber = '1000' as unknown as number
/**
* Non-null assertion operator
*/
interface Home {
foods?: string[]
books?: string[]
}
const MyHome:Home = {}
// !. -> 확실하게 있다는 것을 개발자가 보장(런타임 검사 생략)
MyHome.foods!.push('banana')
MyHome.books!.push('Clean Code')