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"
}