- 함수를 나타내는
function
타입에 대한typescript
인터페이스- 화살표 함수를 사용하는 방식이 더 깔끔
/* basic */ let a: Function // 함수 이외의 값을 할당하면 오류 발생 a = function (): void { console.log("Function Type") } /* arrow function */ let a: (str: string) => string a = function (str: string): string { return str }
- 개념
- 불변하고, 유일한 값을 가지는
symbol
타입에 대한typescript
인터페이스- 특징
object
의key
값으로 사용Object.keys()
나for.in
등과 같은 메소드로접근되지 않기
때문에,hidden 속성(property)
으로 사용 가능- 등장 이유
object
의key
값이문자열
이나정수형
이라면 변경될 위험이 있지만,
symbol
은불변
하고,유일
하기 때문에 변경될 일이 없어서 위험이 없다
(object
에같은 이름
을 가지는프로퍼티
혹은메서드
를 추가하면덮어
씌워질 수 있기 때문)/* Symbol 생성자를 호출함으로써 생성 */ let sym2 = Symbol("key"); let sym3 = Symbol("key"); sym2 === sym3; // false, 심벌은 유일하다. /* Symbol 레지스트리에 존재하는 심볼을 찾고 존재할 경우 이를 반환 */ console.log(Symbol.for('key') === Symbol.for('key')); // true console.log(Symbol('key') === Symbol('key')); // false /* 객체의 key로 활용 */ const sym = Symbol(); let obj = { [sym]: "value", }; console.log(obj[sym]); // "value"
any
- 무엇이든 허용 / 항상 타입 검사를 만족
- 타입을 지정하지 않아도(없어도) 컴파일 오류 발생 X
- 가장 유용하지만, 가장 주의해서 사용해야 한다
unknwon
- 타입을 사용하는 사람이 타입을 지정했는지 확인
- 타입이 없거나 지정돼있지 않으면 컴파일 오류 발생 O
/* any */ let myVar: any = 42 myVar.toFixed() // number은 toFixed() 가 없지만 오류가 발생하지 않음 /* unknown */ let myVar: unknown = 'hello' myVar = 42 // 어떤 타입이든 재할당 가능 let age: number = (myVar as number) // 반드시 사용하려면 타입 명시를 해줘야 한다
- 개념
- 절대 발생할 수 없는 타입을 의미
- 모든 타입의 하위 타입을 의미
-> 어떤 다른 값도 never 타입에 할당 불가- 사용 용도
- 어떤 함수가 어떠한 값도 반환하면 안될 때 사용
- 항상 예외를 발생시키는 함수일 경우 사용
/* 항상 예외를 발생시키는 함수 */ const fetchFriendsOfUser = (username: string): never => { throw new Error('Not Implemented'); } /* 특정 타입 값을 할당받지 않도록 설정 => string을 제외한 타입만 지정 가능 */ type NonString<T> = T extends string ? never : T;
- 리턴 값이 없는 함수를 나타내며, 내부적으로는 undefined를 반환
- 함수 내부에서 return 문이 존재하지 않을 때 적용
let tf: (supply: string) => void tf = function(supply: string) => { console.log(supply) }
- 열거형 데이터 타입
- 값을 할당하지 않으면 차례대로 0부터 증가되며 숫자가 부여됨
enum Color { Red = 'RED', Yellow = 'YELLOW', Green = 'GREEN', Blud = 'BLUE' } let color = Color.Red // color은 Color 타입을 가짐
- 객체를 나타내는 타입
let Dom: { version: string, el: () => void, css: () => void }; Dom = { version: '0.0.1', el(){}, css(){} };
- 유니언 (Union) 타입
- 여러 타입 중 하나일 수 있음을 선언하는 바법
- 합 타입이라고 부르기도 함
- 타입을 확인하기 위해서
typeof
/instanceof
사용function setInfo(id:number|string, name:string){ return { id, name }; } let myVar = number | string | object
- 교차 (Intersection) 타입
- 여러 타입을 하나로 결합한 타입
- 곱 타입이라고 부르기도 함
interface ErrorHandling{ success:boolean; error?:{message:string}; } interface ArtworksData{ artworks:{title:string}[]; } interface ArtistsData{ artists:{name:string}[]; } //이 인터페이스들은 //하나의 에러 핸들링과 자체 데이터로 구성된다. type ArtworkResponse=ArtworksData & ErrorHandling; type ArtistsResponse=ArtistsData & ErrorHandling; const handleArtistsResponse=(response:ArtistsResponse) =>{ if(response.error){ console.error(response.error.message); return; } console.log(response.artists); };
typescript
에서string type
과string literal type
은 구분const
로 선언된 변수는 변경될 일이 없기에string
이 아닌string literal type
으로 간주/* string vs string literal */ const a = "Hello World" // string literal let b = "Hello World" // string const c: string = "Hello World" // string /* 유용한 사용 => 정적인 string literal 로 선언하면 enum과 같이 사용할 수 있음 그리고, 에디터에서도 suggest가 된다 (컴파일 시점에 예외발생 + 오타 감소) */ type EventType = "mouseout" | "mouseover" | "click" function handleEvent(event: EventType) {} handleEvent("click")
- Typescript에서는
string
으로object
를 접근하면오류
가 발생한다 ?
- 기본적으로
TS
에서는 객체(object
)에 접근할 때string literal
로 접근해야 한다- 만약
string type
으로 접근하려면Index Signature
를 선언해줘야 한다/* string type으로 object 접근시 */ const obj = { foo: "hello", } let propertyName = "foo" // string type console.log(obj[propertyName]) // error ! /* index signature 추가 */ const obj = { [index: string] : string foo: "hello", } let propertyName = "foo" // string type console.log(obj[propertyName]) // ok !
- Object 접근 (
javascript
vstypescript
)
Javascript
에서는object
에 접근할 때key
타입이string
이 아니면 내부적으로toString()
을 호출한다 (number
/obj
..)Typescript
에서는 암묵적toString()
이 호출되지 않음
--> JS의 암묵적toString()
은엉망
이라서 호출을 막기 위해TS
에서는 명시적으로toString()
을 하지 않으면오류
를 발생시킴
--> 그래서Index Signature
를 통해 구조를 명시적으로 두어야 한다/* JS에서는 암묵적으로 toString() 호출 O */ let obj = { toString(){ console.log('toString called') return 'Hello' } } let foo:any = {}; foo[obj] = 'World'; // toString called console.log(foo[obj]); // toString called, World console.log(foo['Hello']); // World /* TS에서는 암묵적 toString() 호출 X */ // ERROR: the index signature must be string, number ... foo[obj] = 'World'; // FIX: TypeScript forces you to be explicit foo[obj.toString()] = 'World';
boolean
number
string
undefined
: Javascript에서 초기화되지 않은 값을 의미null
: 초기화 한 후 의도적으로 null값을 할당한 것을 의미bigint
: Number가 안정적으로 나타낼 수 있는 최대치인 2^53-1 보다 큰 정수를 표현할 수 있는 내장 객체 BigInt의 타입- 등
- 사용자가 직접 타입을 정의해서 사용하는 방법은 크게 2가지가 있다
- type alias : type 키워드를 통해 새로운 별칭을 가지는 타입을 정의
- interface : 선언부만 가지는 interface를 통해 객체의 타입을 지정
- 두 방법은 몇가지 차이점이 있으며, 상황에 따라서 선택해서 사용하면 된다
- 확장 방법
- type alias :
&
을 통해 확장- interface :
extends
를 통해 확장/* type */ type PeopleType = { name: string age: number } type StudentType = PeopleType & { school: string } /* interface */ interface PeopleInterface { name: string age: number } interface StudentInterface extends PeopleInterface { school: string }
- 선언적 확장
- type alias : 동일한 type 선언시 Duplicate 오류가 발생
- interace : 기존 interface에 필드가 확장
/* type alias */ type Window = { title: string } type Window = { ts: TypeScriptAPI } // 기존에 동일한 type alias가 존재하므로, duplicate 오류 발생 /* interface */ interface Window { title: string } interface Window { ts: TypeScriptAPI } // Window에는 title과 ts가 모두 존재
- primitive type 형태
- type alias : 가능
- interface : 불가능
/* type alias */ type numberAndObject = number | object /* interface => 반드시 object 형태를 가짐 */ interface temp{ numberAndObject }
- computed value의 사용
- type alias : 가능
- interface : 불가능
// 예제에서 사용되는 in 키워드는 Mapped types임. 즉, 특정 프로퍼티 확인하는 in과 다른 것! /* type alias */ type names = 'firstName' | 'lastName' type NameTypes = { [key in names]: string } /* interface */ const yc: NameTypes = { firstName: 'hi', lastName: 'yc' } interface NameInterface { // error [key in names]: string }
- 상속 / 확장
- type alias :
type alias
끼리extends
,implements
불가능- interface :
interface
는 제한 없음type PersonAlias = { name: string; age: number; }; // type alias /* {interface} extends {type} 가능 */ interface IPerson extends PersonAlias { } /* {class} implements {type} 가능 */ class PersonImpl implements PersonAlias { name: string; age: number; hello() { console.log('안녕하세요'); } } // PersonImpl라는 클래스는 PersonAlias라는 인터페이스를 구현하겠다.
- 합 타입 / 튜플 타입을 반드시 써야되는 상황 -> type alias
- primitive type -> type alias 사용
- 그 외 일반적인 상황 -> interface
- interface는 확장에 대해 열려있기 때문에 Typescript 팀에서 권장
- 컴파일러에게
"이 타입의 특정 타입 임을 단언합니다"
라고 말하는 것- 다른 언어의 타입 캐스트(Cast) 와 비슷하지만, 특별하 검사나 데이터 재구성을 수행하지 않음
- 즉, 오직 컴파일 과정에서만 사용되며, 런타임시 영향을 미치지 않는다
1) 앵글 브라켓 (
<>
) 사용
2)as
키워드 사용let assertion:any = "타입 어설션은 '타입을 단언'합니다."; /* 방법 1) 앵글 브라켓 */ let assertion_count:number = (<string>assertion).length; /* 방법 2) as 키워드 사용 */ let assertion_count:number = (assertion as string).length;
- 피연사자의 데이터 타입을 반환하는 연산자
string
/number
/function
/object
...
typeof null
은object
로 반환된다 ?
JavaScript의 초기 오류
로null 타입체크
가누락
되어 발생되는 문제- JS측에서는 이미 널리 사용되고 있어서 수정을 하지 않겠다고 한다
--> 즉,null
에 대한타입 체크
를별도
로 해주어야 한다
--> 일치 연산자 (===
)를 통해서null
을 체크해야 한다/* typeof */ function checkOutput(myVar: string | number | any){ if(typeof myVar === 'string'){ console.log("string type") }else if(typeof myVar === 'number'){ console.log("number type") }else{ console.log("any type") } } /* object 타입 체크 - null 유의 */ if(yourVariable != null && typeof yourVariable === 'object') {} if(!!yourVariable && typeof yourVariable === 'object') {}
- 클래스의 생성자 함수를 통해서 개체가 특정 class 타입인지 확인하는 방법
- 즉, class의 타입을 확인하기 위한 것
(일반적인 primitive type에 대해서 사용 불가능)class Foo { foo = 123; common = '123'; } class Bar { bar = 123; common = '123'; } function doStuff(arg: Foo | Bar) { if (arg instanceof Foo) { console.log(arg.foo); // 123 console.log(arg.bar); // Error! } if (arg instanceof Bar) { console.log(arg.foo); // Error! console.log(arg.bar); // 123 } } doStuff(new Foo()); doStuff(new Bar());
- 특정 속성(
property
)가 존재하는지 검사하는 연산자interface A { x: number; } interface B { y: string; } function doStuff(q: A | B) { if ('x' in q) { // q: A } else { // q: B } }
type predicates
를 사용해서 직접type guard
역할을 하는 함수를 생성할 수 있다- type predicates
- 형식 :
parameterName
isType
paramegerName
: 현재 함수의 매개변수Type
: 확인할 type/* type predicate를 통해 사용자 정의 type guard 생성 */ function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; } let pet = Fish | Bird if (isFish(pet)) { pet.swim(); } else { pet.fly(); }
- keyof
- object type을 대상으로 key를 허용하는 type을 반환
- enum / object 객체 자체에 대상으로 하면 당연히 수행되지 X
- typeof
- 타입 가드의 종류로서, 확인된 타입을 string으로 반환
- 사용할 수 있는 타입이 정해져 있다
: string / number / object / function ...
string literal
타입정의 시 유용하게 사용
interface
에서는keyof
로 사용enum
/object
에서는keyof typeof
로 사용
/* typeof -> 대상이 X */ /* keyof */ interface Brand { Nike: 'nike'; Adidas: 'adidas'; Puma: 'puma'; } // a은 'Nike' | 'Adidas' | 'Puma' 타입으로 선언된다 let a: keyof Brand
- enum에서
keyof
는 일반적인 객체에서 기대하는 동작과 다르게 동작- 그래서,
keyof typeof
를 함께 사용하도록 공식문서에 안내되어 있다enum LogLevel { ERROR, WARN, INFO, DEBUG, } /* keyof enum */ // enum이 Object니까 Object의 프로퍼티가 들어감 (toString(), replace() ...) type b = keyof LogLevel /* keyof typeof enum */ // LogLevelStrings = 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' 를 가진다 type LogLevelStrings = keyof typeof LogLevel;
- enum 처럼
keyof typeof
를 통해서 string literal 지정 가능let LogLevel = { error : 'ERROR', warn : 'WARN', info : 'INFO', debug : 'DEBUG', } /* keyof typeof enum */ // type LogLevelStrings = 'error' | 'wran' | 'info' | 'debug' 를 가진다 type LogLevelStrings = keyof typeof LogLevel;