Object

홍범선·2023년 10월 23일
0

타입스크립트

목록 보기
22/34

오브젝트 선언법 3가지

바로 오브젝트로 선언하기

const codefactory = {
  name : '코드팩토리',
  age:32
}

인터페이스로 오브젝트 선언하기

interface IPerson{
  name:string;
  age:number;
}
const iPerson: IPerson = {
  name:'아이유',
  age:30
}

타입으로 오브젝트 선언하기

type TPerson = {
  name:string;
  age:number
}
const tPerson: TPerson = {
  name:'유인나',
  age : 30
}

초과 속성 검사

type TName = {
  name: string;
}

type TAge = {
  age:number;
}

const iu = {
  name: '아이유',
  age: 30,
}
// const iu2: TAge = {
//   name: '아이유',
//   age: 30,
// } //에러발생 name에서 에러
// const iu3: TName = {
//   name: '아이유',
//   age: 30,
// }  //에러 발생 age에서 에러
//실제 값을 초기화 할떄 넣으면은 그때는 우리가 정확히 타입에 선언되어 있는대로 프로퍼티를 넣어야 한다.


const testName: TName = iu; 
//이미 선언이 되있는 객체 변수를 다른 변수에다가 할당할 떄 초과되는 값들이 있어도 할당이 가능하다.
const testAge : TAge = iu;

실제 값을 초기화 할 때 타입을 넣으면 그 타입에 선언되어 있는대로 프로퍼티를 넣어야 하지만
이미 선언이 되어있는 객체 변수를 다른 변수에다가 할당할 때 초과되는 값들이 있어도 할당이 가능하다. 일단은 기본적으로 해당 프로퍼티는 가지고 있고 초과되어야 한다.

중첩 객체 안좋은 예

type NestedPerson = {
  identity:{
    name: string;
    age:number;
  },
  nationality:string;
}

const codefactory:NestedPerson = {
  identity:{
    name:'코드팩토리',
    age:32
  },
  nationality : '한국인'
}
//이 프러퍼티 중 하나라도 빼먹으면 에러발생 

NestedPerson에 identity를 바로 선언해버리면 에러 핸들링 측면에서 좋지 안핟.

중첩 객체 좋은 예

type TPerson = {
  identity: TPersonIdentity,
  nationality: string
}

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

const iu: TPerson = {
  identity:{
    name:'아이유',
    age:32
  },
  nationality:"한국인"
}
interface IPerson {
  identity: IPersonIdentity;
  nationality: string;
}

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

const yuJin: IPerson = {
  identity:{
    name:'안유진',
    age:22
  },
  nationality:'한국인'
}

identity객체를 TPerson이라는 타입 예제와 IPerson이라는 인터페이스 예제2를 나누어서 중첩객체를 구현하였는데 이렇게 하면 에러 핸들링 측면에서 좋다.

Optional vs Undefined

interface Dog{
  name : string;
  age : number;
  // 종을 모르면 undefined
  breed? : string;
}

const byulE: Dog = {
  name: '별이',
  age: 12,
  breed: '미니핀' //string | undefined
}

const ori: Dog = {
  name:'오리',
  age: 3
}

Dog에서 breed 프로퍼티를 만약 모른다면 옵셔널로 줄 수 있다. 그러면 breed를 써도 되고 안써도 되는데
breed에 타입은 string | undefined가 된다.

그러면 의문점이 생길 수 있다. interface Dog에서 breed를 옵셔널 대신 string | undefined로 선언한다면 어떻게 될까?

interface Cat{
  name: string;
  age: number;
  breed : string | undefined; // 옵셔널이 아닌 이상 프로퍼티를 절대적으로 입력해주어야 함
}

// const nabi: Cat = {
//   name: '나비',
//   age: 7,
// } //에러발생

const nabi: Cat = {
  name: '나비',
  age: 7,
  breed: undefined
} // 물음표를 넣고 안넣고는 입력해도 되는지 안되는지를 여부를 따지는 거다

옵셔널이 아닌 이상 프로퍼티를 절대적으로 입력해주어야 함
물음표를 넣고 안넣고는 입력해도 되는지 안되는지 여부를 따지는 거다

유추된 객체 타입 유니언

const dogOrCat = Math.random() > 0.5 ?
{
  name:'별이',
  age:12
} : {
  name:'오리',
  breed:'코리안 길냥이'
} // 이렇게 할 경우 그냥 옵셔널 프로퍼티를 만들어 버린다.

dogOrCat.name // string
dogOrCat.age // number | undefined
dogOrCat.breed // string | undefined

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

interface Cat{
  name:string;
  breed:string;
}// 이렇게 직접으로 타입을 명시를 하면 마음대로 가져올 수 없다.

type DogOrCat = Dog | Cat; 

const dogOrCat2: DogOrCat = Math.random() > 0.5 ?
{
  name:'별이',
  age:12
} : {
  name:'오리',
  breed:'코리안 길냥이'
}

dogOrCat2.name;
// dogOrCat2.age; //에러
// dogOrCat2.breed //에러
//쓰는 방법
if('age' in dogOrCat2){
  dogOrCat2; // Dog타입
  dogOrCat2.age
  dogOrCat2.name
}else{
  dogOrCat2 // Cat타입
  dogOrCat2.name
  dogOrCat2.breed
}

객체 인터섹션

type PrimitiveIntersection = string & number; 

never타입이 된다.
왜냐하면 string과 number를 동시에 충족시키는 것은 없기 때문이다.

반면에 객체타입은 다르다.


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

type CompanyType = {
  company : string;
  companyRegistrationNumber: string;
}

type PersonAndCompany = PersonType & CompanyType; 
// 인터섹션은 왼쪽과 오른쪽을 충족시켜야 한다. extension이 된다.

const jisoo: PersonAndCompany = {
  name: '지수',
  age:32,
  company:'YG',
  companyRegistrationNumber:'xxx'
}

PersonType과 CompanyType을 동시에 충족시키는 것은 곧 extension과 같다.
그래서 jisoo변수에 하나라도 프로퍼티가 빠지면 에러가 난다.

type  PetType = {
  petName:string;
  petAge:number;
}

type CompanyOrPet = PersonType & (CompanyType | PetType) 
//PersonType은 무조건 충족시켜야 하고 companyType 또는 PetType을 충족시켜야 한다. 
//이 둘중에서 하나가 충족되었을 때 나머지는 초과 충족되어도 상관없다.

const companyOrPet  : CompanyOrPet = {
  //PersonType
  name:'코드팩토리',
  age : 32,
  // CompanyType
  company : '주식회사 코드팩토리',
  companyRegistrationNumber:'xxxx',

  //PetType
  petName:'오리',
  petAge:32
}

PersonType은 무조건 충족시켜야 하고 companyType 또는 PetType을 충족시켜야 한다.
이 둘중에서 하나가 충족되었을 때 나머지는 초과 충족되어도 상관없다.

오브젝트 key value mapping

enum State {
  loading,
  done,
  error
}

type GlobalApiStatus = {
  getUser: State;
  paginateUsers: State | undefined;
  banUser: State | null;
  getPosts: State;
}

type UserApiStatus = { //만약 GlobalApiStatus에서 변경되면 이것도 변경해주어야 함 번거로움
  getUser: State;
  paginateUsers: State | undefined;
  banUser: State | null;
}

만약 GlobalApiStatus를 따라하는 타입을 UserApiStatus로 만들어보자
GlobalApiStatus를 변경하면 UserApiStatus도 마찬가지로 변경해야한다.
이러면 너무 번거로운 문제점이 있다.

첫 번째 방법

type UserApiStatus2 = {  //GlobalApiStatus의 키값으로 가져오면 된다.
  getUser: GlobalApiStatus['getUser'];
  paginateUsers: GlobalApiStatus['paginateUsers'];
  banUser: GlobalApiStatus['banUser'];
}

두 번째 방법 [in]

type UserApiStatus3 = {
  [k in 'getUser' | 'paginateUsers' | 'banUser']: GlobalApiStatus[k];

}

세 번째 방법 Pick

type PickedUserApiStatus = Pick<GlobalApiStatus, 'getUser' | 'banUser' | 'paginateUsers'>;

네 번째 방법 Omit

type OmitUserApiStatus = Omit<GlobalApiStatus, 'getPosts'>;

다섯 번째 방법 keyof

type KeyOfUserApiStatus = {
  [k in keyof GlobalApiStatus]: GlobalApiStatus[k]; // 모든 키들이 가져옴 마지막꺼도 가져옴
}
type KeyOfUserApiStatus2 = {
  [k in Exclude<keyof GlobalApiStatus, 'getPosts'>]: GlobalApiStatus[k]; // getPosts는 제외하고 가져온다.
}

type KeyOfUserApiStatus3 = {
  [k in Exclude<keyof GlobalApiStatus, 'getPosts'>]?: GlobalApiStatus[k]; // 전체다 옵셔널로 가져온다.
}
interface LoadingStatus {
  type:'loading',
  data : string[];
}

interface ErrorStatus{
  type: 'error',
  message:string
}

type FetchStatus = LoadingStatus | ErrorStatus;

type StatusType = FetchStatus['type']; // "loading" || "error"

}
profile
알고리즘 정리 블로그입니다.

0개의 댓글