인터페이스에서 프로퍼티에 어떤 key 값이 오는지 특정 할 수 없을때
객체에서 key 값을 동적으로 받을때 사용함(프로퍼티 name을 모를때)
interface Arr{
length : number;
[key : number] : string;
} // length 라는 속성을 제외한 속성키는 모두 number임, 속성키에 해당하는 value는 string이여야함.
const arr:Arr = ['3', '5', '7'];
같은 이름으로 여러 인터페이스 선언가능함 그럼 모든 인터페이스가 하나로 합쳐지는 것
따로 병합을 안해줘도 됨 => 다른 사람이 수정해도되는 객체타입을 인터페이스로 선언하면 다른 개발자는 동일한 이름의 인터페이스를 만들어서 수정가능
속성 이름이 겹치는데 타입이 다를 경우 에러발생
인터페이스 선언병합의 단점을 해결함 ( 의도치 않게 합쳐지는 것을 방지함)
네임스페이스 내부타입을 사용하려면 export해야함
namespace Example{
export interface Inner{
test : string;
}
}
const ex1:Example.Inner ={
test: "hiello",
}
namespace Outer{
export namespace Example{
export interface Inner{
test : string;
}
}
}
const ex1:Outer.Example.Inner ={
test: "hiello",
}
네임스페이스 자체를 JS값처럼 사용 할 수 있음
네임스페이스도 이름이 겹치는 경우 병합됨 => 이를 방지하기 위해 묘듈이 존재함.
interface Example{
hello: string;
world?: number; //number|undefined type
readonly wow : boolean;
readonly multiple? : symbol;
}
const example:Example = {
hello: 'hi',
wow : false,
}
example.no; // error 없음
example.wow = true; // error 변경불가능
객체리터럴을 대입했냐 vs 변수를 대입했냐의 여부에 따라 type 검사방식이 달라짐
객체리터럴을 interface 타입으로 선언하면 형태가 똑같아야함, 변수를 인터페이스로 선언하고 대입하면 optional이 됨.
{
interface Example{
hello:string;
}
const example:Example = {
hello:'hi',
why : '나만에러야',
} // 이건 에러
const obj = {
hello:'hi',
why : "나는에러아냐",
}
const example2 :Example = obj; //에러아님
}
함수에서도 동일함.
객체 리터럴을 대입하면 => 잉여속성 검사가 실행됨(타입선언에서 선언하지 않은 속성을 사용할 때 에러를 표시함)
변수를 대입할 때 => 객체 간 대입 가능성을 비교함.
{
interface Money{
amount : number;
unit : string;
}
const money = {amount : 1000, unit:"won", error:"에러아님"}
function addMoney(money1:Money, money2:Money):Money{
return {
amount : money1.amount+money2.amount,
unit : 'won'
}
}
addMoney(money, {amount:3000, unit: "money", error :"에러"}) //error
}
타입추출 => 객체의 속성에 접근해서 type 연동가능
Animal의 속성 type만 변경하면 나머지코드는 신경쓰지 않아도 됨.
{
type Animal = {
name: string;
}
type N1 = Animal['name'];
type N2 = Animal["name"];
type N3 = Animal.name; // error
}
객체 keyof 연산자 : keyof 연산자를 object에 사용하면 object가 가지고 있는 모든 key값을 union type으로 합쳐서 내보내 준다.
key의 type을 얻으면 value의 type도 얻을 수 있다.
배열에 keyof 적용하면 => number | 배열속성이름유니언 | 배열인덱스 문자열유니언
{
const obj = {
hello :"world",
name : "zero",
age : 28,
}
type Keys = keyof typeof obj; // "hello"|"name"|"age"
type Values = typeof obj[Keys]; // value type 뽑기
type Keys1 = keyof any;
type ArrayKeys = keyof [1,2,3];
let a :ArrayKeys = "lastIndexOf";
a = "length";
a = "2";
a = "3"; // 안됨
a = 3
}
{
const obj = {
hello :"world",
name : "zero",
age : 28,
}
type Values = typeof obj["hello" | "name"];
interface Example{
a() :void;
b: () => void;
c:{
():void;
}
}
}
인덱스 시그니처에서 사용 할 수 있는 타입은 string, number, symbol, 템플릿리터럴, 유니언뿐임(결국 인덱스 시그너처를 사용하는 이유 => key를 특정 할 수 없는 경우임)
여기서 리터럴타입과 템플릿리터럴 타입은 다름
type word = "hi";
type word2 = `hi ${word}`
매핑된 객체타입이란? 기존의 다른 타입으로 부터 새로운 객체 속성을 만들어내는 타입
매핑된 타입은 key의 조합을 사용해 키를 통해 탕입을 반복적으로 생성하는 제너릭 타입임.
in 연산자 사용함 => in연산자 오른쪽에는 유니언타입이 와야함. (속성이 하나하나 평가되어 type으로 변환됨)
기존 객체타입을 복사할때 많이 사용함.
{
type HelloAndHi = {
[key : "hello"| "hi"] : string;
}
type HelloAndHi2 = {
[key in "hello"| "hi"] : string;
}
interface Original{
name : string;
age : number;
married : boolean;
}
type Copy = {
[key in keyof Original] : Original[key];
}
type Tuple = [1,2,3];
type CopyTuple = {
[key in keyof Tuple] : Tuple[key];
}
// Tuple 객체의 전체 type 복사함.
type CopyTuple2 = Tuple // 이건 [1,2,3] tuple타입으로
}
기존의 객체 type을 복사할때, 수식어를 추가하거나 제거가능함. as 키워드를 통해 속성이름또한 변경가능
(-)의 의미는 속성을 제거함.
{
interface Original{
name : string;
age : number;
married : boolean;
}
type Copy = {
-readonly [key in keyof Original]? : Original[key];
}
}
유니온 => 합집합, intersection => 교집합
전체집합은 unknown, 공집합은 never 개념, 항상 좁은타입에서 넓은타입으로 대입해야함. (좁은타입은 넓은타입에 대입가능)