宮水の日記

宮水の日記

主に書評や資格取得について記事を書いています。

Typescript exercises やってみた 7問目

みなさんこんにちは。宮水です。
今日は、TypeScript エクササイズの7に取り組んでみました。
翻訳はGoogle翻訳にぶち込みました。

こちらのリポジトリをforkして、cloneして取り組みます。
rootディレクトリで、yarn installしてから問題文にあるRun this exerciseのコマンドを叩くと、答え合わせができます。すごい!!
github.com

本日の問題

import chalk from 'chalk';

/*

Intro:

    Project grew and we ended up in a situation with
    some users starting to have more influence.
    Therefore, we decided to create a new person type
    called PowerUser which is supposed to combine
    everything User and Admin have.

  プロジェクトが成長し、一部のユーザーがより大きな影響力を持つようになる状況になりました。
  そのため、UserとAdminが持つすべてのものを組み合わせることになっているPowerUserという新しい人物タイプを作成することにしました。

Higher difficulty exercise:

    Define type PowerUser which should have all fields
    from both User and Admin (except for type),
    and also have type 'powerUser' without duplicating
    all the fields in the code.

  UserとAdminの両方からのすべてのフィールドが必要なタイプPowerUserを定義します(typeを除く)。
  また、コード内のすべてのフィールドを複製することなく、タイプ 'powerUser'を持ちます。

Run:

    npm run 7

    - OR -

    yarn -s 7

*/

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

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

type PowerUser = unknown;

type Person = User | Admin | PowerUser;

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: 'powerUser',
        name: 'Nikki Stone',
        age: 45,
        role: 'Moderator',
        occupation: 'Cat groomer'
    }
];

function isAdmin(person: Person): person is Admin {
    return person.type === 'admin';
}

function isUser(person: Person): person is User {
    return person.type === 'user';
}

function isPowerUser(person: Person): person is PowerUser {
    return person.type === 'powerUser';
}

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

console.log(chalk.yellow('Admins:'));
persons.filter(isAdmin).forEach(logPerson);

console.log();

console.log(chalk.yellow('Users:'));
persons.filter(isUser).forEach(logPerson);

console.log();

console.log(chalk.yellow('Power users:'));
persons.filter(isPowerUser).forEach(logPerson);

// In case if you are stuck:
// https://www.typescriptlang.org/docs/handbook/utility-types.html

答え

まず問題の意味を読み解くのに時間がかかりました😇
interface powerUserは作らずに、UserもAdminの特性をもつpowerUser型を作りましょうという意味でした。
つまり、UserだけがもつoccupationもAdminだけがもつroleも持ちます。

  // ココニカイテアッタヨ
    {
        type: 'powerUser',
        name: 'Nikki Stone',
        age: 45,
        role: 'Moderator',
        occupation: 'Cat groomer'
    }

今回のヒントはこちらです。
TypeScript: Handbook - Utility Types

TypeScript provides several utility types to facilitate common type transformations. These utilities are available globally.

ユーティリティ型は、一般的な型変換を容易にしてくれるTypeScriptが用意してくれた型のことだそうです。

今回は、Omitだけ馴染みがあったので、Omitを使いました。

Userのtypeを取り除く & Adminのtypeを取り除く & type: powerUserを追加するという意味の型を作りました。

type PowerUser = Omit<User, 'type'> & Omit<Admin, 'type'> & {type: 'powerUser'};

多分いい感じだと思います。
f:id:kattyan53:20200805084901p:plain

他に回答あったらご教示いただけると嬉しいです!以上です!