みなさんこんにちは。宮水です。
今日は、TypeScript エクササイズの2に取り組んでみました。
英語も苦手なので、翻訳も自分でしてみました。
こちらのリポジトリをforkして、cloneして取り組みます。
rootディレクトリで、yarn installしてから問題文にあるRun this exerciseのコマンドを叩くと、答え合わせができます。
github.com
本日の問題
import chalk from 'chalk'; /* Intro: Since we already have some of the additional information about our users, it's a good idea to output it in a nice way. 私たちはすでにいくつか情報を追加されたユーザーを持っているので、適切な方法で出力しましょう。 Exercise: Fix type errors in logPerson function. logPerson function should accept both User and Admin and should output relevant information according to the input: occupation for User and role for Admin. logPerson関数の型エラーを修正してください。 logPerson関数は、UserとAdminの両方を受け入れ、Userの職業とAdminの役割を出力する必要があります。 Run: npm run 2 - OR - yarn -s 2 */ interface User { name: string; age: number; occupation: string; } interface Admin { name: string; age: number; role: string; } type Person = User | Admin; const persons: Person[] = [ { name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' }, { name: 'Jane Doe', age: 32, role: 'Administrator' }, { name: 'Kate Müller', age: 23, occupation: 'Astronaut' }, { name: 'Bruce Willis', age: 64, role: 'World saver' } ]; function logPerson(person: Person) { let additionalInformation: string; if (person.role) { additionalInformation = person.role; } else { additionalInformation = person.occupation; } console.log(` - ${chalk.green(person.name)}, ${person.age}, ${additionalInformation}`); } persons.forEach(logPerson); // In case if you are stuck: // https://www.typescriptlang.org/docs/handbook/advanced-types.html#using-the-in-operator
答え
interface User { type: 'user'; name: string; age: number; occupation: string; } interface Admin { type: 'admin'; name: string; age: number; role: string; } type Person = User | Admin; 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' } ]; function logPerson(person: Person) { let additionalInformation: string; if (person.type === 'admin') { additionalInformation = person.role; } else { additionalInformation = person.occupation; } console.log(` - ${chalk.green(person.name)}, ${person.age}, ${additionalInformation}`); } persons.forEach(logPerson);
んー、ちょっと微妙なコードだと思うけど、一応動きました。
感想
今回は、Type Guardの問題だと思います。
今回の問題では、User型にroleプロパティが、Admin型にoccupationプロパティがないという型エラーが出ていました。
(ちなみに、前回の問題では、roleもoccupationも参照していなかったので、エラーにはなりませんでした。)
そこで、型定義にuser か adminかわかる情報を追加してあげて、データにもtypeプロパティを追加し、
if文でtypeプロパティごとに出力する値を変えるような処理を入れました。
この問題はいろんな書き方ができると思います。
初めは、typeofやinstanceofをそれを使おうと思ったのですが、使えませんでした。
typeofは、stringやnumberやnullといった基本型に対して使えるものです。
instanceofは、interfaceで定義された型に対しては使うことができません。
interfaceのタイプガードをするとしたら、以下のように自分でタイプガードを定義しないといけないようです。(ユーザー定義タイプガードというそうです。)
interface Foo { foo: number } interface Bar { bar: number } // ユーザ定義タイプガード function implementsFoo(arg: any): arg is Foo { return arg !== null && typeof arg === "object" && typeof arg.foo === "number" } function getNumber(arg: Foo | Bar): number { if (implementsFoo(arg)) { // implementsFooをタイプガードに使用 return arg.foo } else { return arg.bar } } ※ 参考のQiitaから引用しました
以上です!
参考:TypeScript: interfaceにはinstanceofが使えないので、ユーザ定義タイプガードで対応する - Qiita