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를 나누어서 중첩객체를 구현하였는데 이렇게 하면 에러 핸들링 측면에서 좋다.
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을 충족시켜야 한다.
이 둘중에서 하나가 충족되었을 때 나머지는 초과 충족되어도 상관없다.
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'];
}
type UserApiStatus3 = {
  [k in 'getUser' | 'paginateUsers' | 'banUser']: GlobalApiStatus[k];
}
type PickedUserApiStatus = Pick<GlobalApiStatus, 'getUser' | 'banUser' | 'paginateUsers'>;
type OmitUserApiStatus = Omit<GlobalApiStatus, 'getPosts'>;
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"
}