StudyForYou / ouahhan-typescript-with-react

우아한 타입스크립트 with 리액트 스터디 레포 🧵
4 stars 0 forks source link

#18 [5장_3] 주어진 코드에서 유틸리티 타입을 활용해 filterUsers의 타입을 올바르게 수정해보아요! #30

Closed hyeyoonS closed 2 months ago

hyeyoonS commented 2 months ago

❓문제

주어진 코드에서 유틸리티 타입을 활용해 filterUsers의 타입을 올바르게 수정해보아요!

interface User {
    type: 'user';
    name: string;
    age: number;
    occupation: string;
}

interface Admin {
    type: 'admin';
    name: string;
    age: number;
    role: string;
}

export type Person = User | Admin;

export const persons: Person[] = [
    { type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
    {
        type: 'admin',
        name: 'Jane Doe',
        age: 32,
        role: 'Administrator'
    },
    {
        type: 'user',
        name: 'Kate Müller',
        age: 23,
        occupation: 'Astronaut'
    },
    {
        type: 'admin',
        name: 'Bruce Willis',
        age: 64,
        role: 'World saver'
    },
    {
        type: 'user',
        name: 'Wilson',
        age: 23,
        occupation: 'Ball'
    },
    {
        type: 'admin',
        name: 'Agent Smith',
        age: 23,
        role: 'Administrator'
    }
];

export const isAdmin = (person: Person): person is Admin => person.type === 'admin';
export const isUser = (person: Person): person is User => person.type === 'user';

export function logPerson(person: Person) {
    let additionalInformation = '';
    if (isAdmin(person)) {
        additionalInformation = person.role;
    }
    if (isUser(person)) {
        additionalInformation = person.occupation;
    }
    console.log(` - ${person.name}, ${person.age}, ${additionalInformation}`);
}

export function filterUsers(persons: Person[], criteria: User): User[] {
    return persons.filter(isUser).filter((user) => {
        const criteriaKeys = Object.keys(criteria) as (keyof User)[];
        return criteriaKeys.every((fieldName) => {
            return user[fieldName] === criteria[fieldName];
        });
    });
}

console.log('Users of age 23:');

filterUsers(
    persons,
    {
        age: 23
    }
).forEach(logPerson); //🚨 Error: Argument of type '{ age: number; }' is not assignable to parameter of type 'User'. Type '{ age: number; }' is missing the following properties from type 'User': type, name, occupation

🎯답변

🤔 Partial이 뭐지?

export function filterUsers(persons: Person[], criteria: Partial<User>): User[] {
    return persons.filter(isUser).filter((user) => {
        const criteriaKeys = Object.keys(criteria) as (keyof User)[];
        return criteriaKeys.every((fieldName) => {
            return user[fieldName] === criteria[fieldName];
        });
    });
}

이렇게 수정하면

filterUsers(
    persons,
    {
        age: 23
    }
).forEach(logPerson);
filterUsers(
    persons,
    {
        name: "Wilson"
    }
).forEach(logPerson);

이렇게 일부 속성이 일치하는 user을 필터할 수 있습니다.

drizzle96 commented 2 months ago
export function filterUsers(persons: Person[], criteria: Partial<User>): User[] {
  return persons.filter(isUser).filter((user) => {
    const criteriaKeys = Object.keys(criteria) as (keyof User)[]
    return criteriaKeys.every((fieldName) => {
      return user[fieldName] === criteria[fieldName]
    })
  })
}
qooktree1 commented 2 months ago
// filterUsers 함수는 결론적으로 2번째 인자로 age 객체를 보내고 그것을 User객체로 받기 때문에 오류가 난다.
// `User` 타입의 모든 속성이 존재해야지 해당 함수가 실행되게 된다.
// 따라서 부분집합의 개념인 `Partial` 유틸리티 함수를 사용한다.
export function filterUsers(persons: Person[], criteria: Partial<User>): User[] {
    return persons.filter(isUser).filter((user) => {
        const criteriaKeys = Object.keys(criteria) as (keyof User)[];
        return criteriaKeys.every((fieldName) => {
            return user[fieldName] === criteria[fieldName];
        });
    });
}

filterUsers(persons, { age: 23 }).forEach(logPerson);