Posts [TypeScript] 타입스크립트 Utility types (2) - Partial, Required, Record
Post
Cancel

[TypeScript] 타입스크립트 Utility types (2) - Partial, Required, Record

타입스크립트 Utility types (2) - Partial, Required, Record


타입스크립트에서는 type transformation을 유연하게 도와 주는 여러 유틸리티 타입을 제공하고 있습니다. (타입스크립트 유틸리티 타입)

이번에는 타입스크립트의 Partial, Required, Pick, Record에 대해 알아보겠습니다.


Partial<Type>

Partial 타입은 어떤 타입의 모든 프로퍼티를 옵셔널(optional)로 바꾸는 기능을 합니다. 바로 예시로 확인해 보겠습니다.

아래 예시는 업데이트 할 값을 인자로 전달받고, 새로운 객체로 반환하는 함수가 있는 단순한 예시입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface Profile {
  // Profile 타입은 아래 nickName, age, address 프로퍼티가 전부 필수
  nickName: string;
  age: number;
  address: string;
}

const prevProfile = {
  nickName: "John",
  age: 20,
  address: "test Address",
};

const updateProfile = (prevData: Profile, updateData: Profile) => {
  return { ...prevData, updateData };
};

// ts(2345) error : Type '{ nickName: string; }' is missing the following properties from type 'Profile': age, address
const newProfile = updateProfile(prevProfile, { nickName: "Smith" });

updateProfile 함수의 파라미터에는 각각 이전 데이터와, 바꾸려는 값을 전달하고 있습니다. 위 코드는 Profile 데이터 중 nickName만 바꾸고 싶은 상황을 가정한 것입니다.

그러나 updateProfile 함수의 updateData 파라미터는 타입이 Profile 입니다. Profile 타입은 nickName, age, address 프로퍼티를 모두 가져야만 하는데, 호출 부분에서 {nickName: 'Smith'}로 nickName 값만 전달하고 있으니 당연히 오류가 발생합니다.

이럴 때 Profile 타입의 프로퍼티들을 optional로 바꾼다면 좋을 텐데, 그 역할을 Partial이 한번에 해 줍니다.

1
2
3
4
5
6
interface Profile {
  nickName?: string;
  age?: number;
  address?: string;
}
// 위처럼 필드 하나하나 옵셔널을 달아준 타입을 새로 정의하지 않아도, Partial<Profile> 을 사용하면 동일합니다.
1
2
3
4
5
6
const updateProfile = (prevData: Profile, updateData: Partial<Profile>) => {
  return { ...prevData, updateData };
};

// OK!
const newProfile = updateProfile(prevProfile, { nickName: "Smith" });

Required<Type>

Required는 Partial과는 반대로, 모든 프로퍼티를 required로 바꿔 줍니다. 쉽게 말해 모든 프로퍼티에서 옵셔널을 빼는 역할을 합니다.

1
2
3
4
5
interface Comment {
  title: string;
  description?: string;
  like?: number;
}

Comment interface는 title은 필수고, description과 like는 옵셔널로 정의되어 있습니다.

Required<Comment>는 이미 required 속성이었던 title은 볼 필요도 없고, description, like 모두 옵셔널을 빼버립니다. 그러므로 title, description, like 값 모두가 필요하게 됩니다.

어떤 상황에서 쓸 수 있을까요? 예를 들자면, 처음 값을 받아올 때는 몇몇 프로퍼티가 없을 수 있지만, DB에 저장할 때는 꼭 모든 프로퍼티를 저장해야 하는 상황이 생길 수 있습니다.

값을 저장할 때 모든 프로퍼티를 받았는지는 Required를 활용하면 손쉽게 검증 가능할 것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Comment {
  title: string;
  description?: string;
  like?: number;
}

const getData = () => {
  const commentData = db.get('comment') as Comment;
  return commentDatas;
}

const saveData = (comment: Required<Comment>) => {
  return db.save(comment)
}

// ts(2345) error : Type '{ title: string; }' is missing the following properties from type 'Required<Comment>': description, like
const result = saveData({title: 'John'});

saveData 함수에 description, like 값을 넘기지 않아 에러가 발생하는 모습입니다.


Record<Keys, Type>

마지막으로 볼 타입은 Record 입니다. 키가 Keys, 값이 Type인 객체 형태의 타입을 정의합니다.

즉, 예를 들어 Record<string, any> 타입은 key 값에는 string 타입만 가능하고, value에는 아무 타입이나 가능하다 는 의미가 됩니다.

1
2
3
4
5
6
7
const myData: Record<string, any> = {
  name: "John",
}; // OK

const myData2: Record<string, any> = {
  age: 1234,
}; // OK

key나 value에 union type이 와도 됩니다. 즉, key나 value의 값에 가능한 집합만 오도록 할 수 있습니다.

key 값은 학생들 이름, 그리고 value에는 간단한 정보를 포함한 변수를 정의해 볼 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
type Student = "John" | "James" | "Smith";
interface StudentInfo {
  age: number;
  address: string;
  major?: number;
}

// 아래 John, James, Smith 중 하나라도 빠지면 에러 발생
let info: Record<Student, StudentInfo> = {
  John: { age: 20, address: "test1" },
  James: { age: 22, address: "test12" },
  Smith: { age: 23, address: "test123" },
};

이처럼 Record의 객체의 타입을 정의하는 특성을 토대로 여러 상황에서 유용하게 사용할 수 있습니다.

이번 포스팅을 리뷰하는 느낌으로 아래 내용을 확인해 주세요.

위의 info 변수에 저장하고 있는 객체의 value 타입이 현재는 StudentInfo로, age와 address은 필요하나 grade 값은 옵셔널입니다.

만약 value에 age, address, grade 세 프로퍼티를 모두 필수로 받고 싶다면, 어떻게 수정하면 될까요?

오늘 다룬 Required를 활용하면 됩니다.

1
2
3
4
5
let info: Record<Student, Required<StudentInfo>> = {
  John: { age: 18, address: "test1", major: "computer science" },
  James: { age: 19, address: "test12" }, // error
  Smith: { age: 20, address: "test123" }, // error
};

레퍼런스

일렉트론(Electron) - 메인 프로세스와 렌더러 프로세스(main process, renderer process)

[CSS] 웹뷰에서 롱프레스 시 발생하는 기본 동작들 막기(드래그, 컨텍스트 메뉴)