みなさんこんにちは。宮水です。
今日は、TypeScript エクササイズの5に取り組んでみました。
英語も苦手なので、翻訳も自分でしてみました。
こちらのリポジトリをforkして、cloneして取り組みます。
rootディレクトリで、yarn installしてから問題文にあるRun this exerciseのコマンドを叩くと、答え合わせができます。
github.com
本日の問題
import chalk from 'chalk'; /* Intro: Filtering requirements have grown. We need to be able to filter any kind of Persons. フィルタリングの要件が増えました。 あらゆる種類のPersonをフィルタリングできるようにする必要があります。 Exercise: Fix typing for the filterPersons so that it can filter users and return User[] when personType='user' and return Admin[] when personType='admin'. Also filterPersons should accept partial User/Admin type according to the personType. filterPersons の入力を修正して、ユーザーをフィルタリングし、personType = 'user'のときにUser[]を返し、personType = 'admin'のときにAdmin[]を返すようにしてください。 また、filterPersonsは、personTypeに応じて部分的なUser/Adminタイプを受け入れる必要があります。 Higher difficulty bonus exercise: Implement a function `getObjectKeys()` which returns proper type for any argument given, so that you don't need to cast it. let criteriaKeys = Object.keys(criteria) as (keyof User)[]; --> let criteriaKeys = getObjectKeys(criteria); Run: npm run 5 - OR - yarn -s 5 */ 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' }, { type: 'user', name: 'Wilson', age: 23, occupation: 'Ball' }, { type: 'admin', name: 'Agent Smith', age: 23, role: 'Anti-virus engineer' } ]; function logPerson(person: Person) { console.log( ` - ${chalk.green(person.name)}, ${person.age}, ${person.type === 'admin' ? person.role : person.occupation}` ); } function filterPersons(persons: Person[], personType: string, criteria: unknown): unknown[] { return persons .filter((person) => person.type === personType) .filter((person) => { let criteriaKeys = Object.keys(criteria) as (keyof Person)[]; return criteriaKeys.every((fieldName) => { return person[fieldName] === criteria[fieldName]; }); }); } let usersOfAge23: User[] = filterPersons(persons, 'user', { age: 23 }); let adminsOfAge23: Admin[] = filterPersons(persons, 'admin', { age: 23 }); console.log(chalk.yellow('Users of age 23:')); usersOfAge23.forEach(logPerson); console.log(); console.log(chalk.yellow('Admins of age 23:')); adminsOfAge23.forEach(logPerson); // In case if you are stuck: // https://www.typescriptlang.org/docs/handbook/functions.html#overloads
私の答え
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' }, { type: 'user', name: 'Wilson', age: 23, occupation: 'Ball' }, { type: 'admin', name: 'Agent Smith', age: 23, role: 'Anti-virus engineer' } ]; function logPerson(person: Person) { console.log( ` - ${chalk.green(person.name)}, ${person.age}, ${person.type === 'admin' ? person.role : person.occupation}` ); } function filterPersons(persons: Person[], personType: 'admin', criteria: Partial<Admin>): Admin[]; function filterPersons(persons: Person[], personType: 'user', criteria: Partial<User>): User[]; function filterPersons(persons: Person[], personType: string, criteria: Partial<Person>): Person[] { return persons .filter((person) => person.type === personType) .filter((person) => { let criteriaKeys = Object.keys(criteria) as (keyof Person)[]; return criteriaKeys.every((fieldName) => { return person[fieldName] === criteria[fieldName]; }); }); } let usersOfAge23: User[] = filterPersons(persons, 'user', { age: 23 }); let adminsOfAge23: Admin[] = filterPersons(persons, 'admin', { age: 23 }); console.log(chalk.yellow('Users of age 23:')); usersOfAge23.forEach(logPerson); console.log(); console.log(chalk.yellow('Admins of age 23:')); adminsOfAge23.forEach(logPerson); // In case if you are stuck: // https://www.typescriptlang.org/docs/handbook/functions.html#overloads
感想
今回は、オーバーロードの問題でした。
同じ名前のメソッドでも、引数が違うか、引数の型が違えば定義できるというものです。
中身も同じ関数を書かないといけないと思ったのですが、型だけオーバーロードしてくれるんですね。
(コンパイラーが型チェックできるからOKって意味だと思う...)
The answer is to supply multiple function types for the same function as a list of overloads. This list is what the compiler will use to resolve function calls. Let’s create a list of overloads that describe what our pickCard accepts and what it returns.
昨日、同じチームの先輩から助言を受けて、以下の部分は、
function filterPersons(persons: Person[], personType: 'admin', criteria: {[P in keyof Admin]?: Admin[P];}): Admin[]; function filterPersons(persons: Person[], personType: 'user', criteria: {[P in keyof User]?: User[P];}): User[]; function filterPersons(persons: Person[], personType: string, criteria: {[P in keyof Person]?: Person[P];}): Person[] {
Partial<型>で置き換えられるとと教えていただいて、使ってみました。
function filterPersons(persons: Person[], personType: 'admin', criteria: Partial<Admin>): Admin[]; function filterPersons(persons: Person[], personType: 'user', criteria: Partial<User>): User[]; function filterPersons(persons: Person[], personType: string, criteria: Partial<Person>): Person[] {
以上です!