Slide 1

Slide 1 text

+BO )BSBKVLVUT.FFUVQ !PLVOPLFOUBSP ίʔ υʹ͸ܕΞϊςʔγϣϯΑ Γ ΋ 
 ཁ݅ΞϊςʔγϣϯΛ૿΍ͤ ʂ

Slide 2

Slide 2 text

୭ w Ԟ໺ݡଠ࿠!PLVOPLFOUBSP w ΫϨε΢ΣΞגࣜձࣾ w 5ZQF4DSJQUྺ೥ w ϑϩϯ τΤϯ υ ɾ όοΫΤϯ υ

Slide 3

Slide 3 text

5ZQF4DSJQUొஃͷྺ࢙

Slide 4

Slide 4 text

5ZQF4DSJQUొஃͷྺ࢙ ࠓ೔͸͜Εͷଓ͖Ͱ͢ˢ

Slide 5

Slide 5 text

ͳͥ5ZQF4DSJQUΛ࢖͏ͷ͔ ʁ w +BWB4DSJQU͸ॻ͍͍ͯͯϛεΛ͢Δݴޠ͔ͩΒͰ͋Δ w ΋ͪΖΜ5ZQF4DSJQU΋ॻ͍͍ͯͯϛεΛ͢ΔݴޠͰ͋Δ w ϛε͔ͯ͠Βϛεʹؾ෇͘ ·Ͱͷ͕࣌ؒ୹͚Ε͹୹͍΄Ͳخ͍͠ w ͦ΋ͦ΋ϛε͠ͳ͍Α ͏ͳߏจͰ͋Ε͹΋ͬͱخ͍͠ wʮઈରʹϛεΛ͠ͳ͍ਓʯ ͕։ൃ͢ΔͳΒ+BWB4DSJQUͷ··͓޷͖ʹͲ͏ ͧ

Slide 6

Slide 6 text

ਓ͸ϛεΛ͢ΔΜͰ͢ ʂ

Slide 7

Slide 7 text

ۃ୺ͳྫ ͜Ε͸+BWB4DSJQUͷίʔ υ 
 ͭͷ਺஋Λ଍ͯ͠߹ܭΛฦ͢add()ؔ਺ function add(a, b) { return a + b; }

Slide 8

Slide 8 text

ۃ୺ͳྫ Ͱ΋ add("Hello", "World") ͳΜͯ͜ͱ΋Ͱ͖Δ 
 ʢ݁Ռ͸)FMMP8PSMEʣ function add(a, b) { return a + b; }

Slide 9

Slide 9 text

ۃ୺ͳྫ function add(a, b) { return a + b; } ࣮૷ऀ͸਺஋ͷ଍͠ࢉΛ૝ఆ࣮ͯ͠૷ͨ͠ͷʹɺ 
 ར༻ऀ͸͜ͷؔ਺Λจࣈྻͷ࿈݁ͷͨΊͩͱޡղͯ͠͠·ͬͨ ʂ

Slide 10

Slide 10 text

ҙࢥૄ௨ͷϛε w ίʔ υΛॻ͍͍ͯΔ্Ͱͷϛε͸UZQPɺ ͏ ͔ͬΓ࡟আͳͲ༷ʑ w ։ൃऀಉ࢜ͷҙࢥૄ௨͕ίʔ υ্Ͱ׬݁͠ͳ͍͜ͱ΋ 
 ҙࢥૄ௨ͷϛεͱݺ΂Α ͏ w จࣈྻ࿈݁ؔ਺ͱޡղͯ͠͠·ͬͨ։ൃऀ͸ɺ 
 add()ؔ਺͕Ͳ͏͍͏ؔ਺ͳͷ͔࣮૷ऀຊਓʹฉ͘ ·Ͱ೺ѲͰ͖ͳ͔ͬͨ

Slide 11

Slide 11 text

ଞਓͷίʔ υɺ ਖ਼͠ ͘ղऍͯ͠ಡΜͰ·͔͢ ʁ w ΋ͪΖΜ͜Ε͸͘͢͝ۃ୺ͳྫ 
 ʢ+ ͸਺஋ͷՃࢉʹͷΈ༻͍ͯɺ จࣈྻ࿈݁͸ String#join() ΍5FNQMBUFMJUFSBMTΛ࢖͏ํ͕Α Γ਌੾ʣ w ͔͠͠ɺ ۀ຿͸͜͏͍ͬͨ ʮଞਓͷίʔ υΛᴥᴪͳ͘ղऍͰ͖͍ͯΔ͔ʯ ͱ͍͏ 
 ධՁͷ࿈ଓͰ͋Δ w ίʔ υதͷ৘ใྔ͕๡͍͠ͱɺ ͨͱ͑1BSTBCMFͳίʔ υͰ͋ͬͨͱͯ͠΋ 
 ଞͷ։ൃऀͷҙਤ·Ͱղऍ͢Δͷ͸೉͍͠

Slide 12

Slide 12 text

ܕΞϊςʔγϣϯ function add(a: number, b: number): number { return a + b; } 5ZQF4DSJQUͰ͸ɺ Ҿ਺΍໭Γ஋ͷܕΛදه͢Δ 
 ͜ΕΛܕΞϊςʔγϣϯ 5ZQFBOOPUBUJPOT ͱ͍͏

Slide 13

Slide 13 text

ܕΞϊςʔγϣϯ function add(a: number, b: number): number { return a + b; } ܕΞϊςʔγϣϯͷ͓͔͛Ͱɺ 5ZQF4DSJQUίϯύΠ ϥ͚ͩͰͳ͘ 
 ίʔ υΛಡΉਓؒʹ΋ҙਤΛղऍ͢Δखॿ͚ͱͳΔ ʮ͋ɺ ͜Ε͸number͔ͩΒจࣈྻ࿈݁ʹ࢖͏ؔ਺͡Όͳ͍ͳ ʂ ʯ

Slide 14

Slide 14 text

ʜͱ͍͏࿩͸೥લޙʹࢄʑ͞Ε· ͨ͠

Slide 15

Slide 15 text

೥͸ܕΞϊςʔγϣϯॻ͍ͯ·͢ΑͶ w ͕͢͞ʹ5ZQF4DSJQU΋όʔδϣϯYʹಥೖ͠੒ख़͖ͯͨ͠ w पΓͰॻ͍͍ͯΔ։ൃऀ΋͔ͳΓ૿͑ͨ w --noImplicitAny --noImplicitReturns ͳͲͷ 
 UTDPO fi H$PNQJMFS0QUJPOTͷ͓͔͛Ͱ 
 Ξϊςʔγϣϯͷॻ͖࿙ΕΛ๷͙ํ๏΋௕೥ཱ֬͞Ε͍ͯΔ

Slide 16

Slide 16 text

ܕΞϊςʔγϣϯ͸Έͳ͞Μॻ͍͍ͯΔͱࢥ͍·͢ͷͰɺ 
 ͜Ε͔Β͸ ʮཁ݅Ξϊςʔγϣϯʯ Λॻ͍ͯΈ·ͤΜ͔

Slide 17

Slide 17 text

ཁ݅Ξϊςʔγϣϯ ʁ w ຊߘͷ଄ޠ w ͪΐͬͱΩϟονʔͳλΠ τϧʹ͔͚ͨͬͨͩ͠ͳͷͰ 
 άάͬͯ΋ެࣜͷ υΩϡϝϯ τͱ͔͸ग़͖ͯ·ͤΜ w ܕΞϊςʔγϣϯʹܕҎ্ͷ৘ใྔΛ΋ͨͤΔ͜ͱͰ 
 ೝࣝͷᴥᴪΛΑ Γແ͘ ͢͜ͱΛ໨తͱ͢Δͷ͕ຊ࣭

Slide 18

Slide 18 text

͜ ͏͍͏ ͜ͱɺ ͨ͜͠ͱ͋Δਓ type UserId = string; type BookId = string; ຊΛ؅ཧ͢ΔΞϓϦ 
 UserId΋BookId΋จࣈྻͳΜ͚ͩͲ۠ผ͍ͨ͠ʜ

Slide 19

Slide 19 text

type UserId = string; type BookId = string; async function fetchBook(id: BookId): Promise { // } export function BookCard(): JSX.Element { const [book, setBook] = useState(null); useEffect(() => { (async (): Promise => { const result = await fetchBook(); setBook(result); })(); }, []); return <>>; } ͜Μͳঢ়گ͕͋ͬͨͱ͠·͠ΐ ͏

Slide 20

Slide 20 text

type UserId = string; type BookId = string; async function fetchBook(id: BookId): Promise { // } export function BookCard(): JSX.Element { const [book, setBook] = useState(null); useEffect(() => { (async (): Promise => { const result = await fetchBook(); setBook(result); })(); }, []); return <>>; } fetchBook()ؔ਺ʹ͸BookIdܕͷҾ਺Λ౉ͤ͹Αͦ͞͏ Ͱ͢

Slide 21

Slide 21 text

const id: BookId = 'ckyx69gli000009ju65mvhyw7'; const result = await fetchBook(id); Αͦ͞͏

Slide 22

Slide 22 text

const id: UserId = 'ckyx6c8xc000009jm8acefv2v'; const result = await fetchBook(id); ຊ౰ʹ ʁ ʁ ʁ 
 ίϯύΠϧ௨ͬͯΔΑ ʁ ʁ ʁ

Slide 23

Slide 23 text

const result = await fetchBook('͜Μʹͪ͸'); ͳΜͳΒ͜Ε΋௨Δ

Slide 24

Slide 24 text

ίϯύΠ ϥ͸۠ผ͠·ͤΜ w typeΛ࢖ͬͯstringʹผ໊Λ͚ͭͯ΋ 
 ͦΕ͸͋͘ ·Ͱ΋ΤΠ ϦΞε ʢผ໊ʣ ʹ͗͢ͳ͍ w ͳͷͰυΩϡϝϯ τʹ΋ͪΌΜͱ5ZQF"MJBTFTͱॻ͔Ε͍ͯΔ 
 https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases w ਓؒʹ͸ผ໊Ͱ͋Δ͜ͱ͸ࣝผͰ͖Δ͕ 
 ίϯύΠ ϥʹ͸stringʹผ໊Λ෇͚ͨ͜ͱ͔͠఻Θ͍ͬͯͳ͍ w UserIdͱBookId͕ޓ͍ʹstringͷผ໊Ͱ͋Δͱ͍͏͚ͩͰ 
 ͦΕΒ͕ޓ͍ʹ۠ผ͞Ε͏ Δ΂͖ͱ͍͏ཁ݅·Ͱ͸ίϯύΠ ϥʹ఻Θ͍ͬͯͳ͍

Slide 25

Slide 25 text

ͳΒ͹Ϋϥε͔ ʁ class UserId { constructor(public readonly value: string) {} } class BookId { constructor(public readonly value: string) {} } UserIdͱBookIdΛͦΕͧΕclassͰએݴͯ͠Έͨ

Slide 26

Slide 26 text

const id = new BookId('ckyx69gli000009ju65mvhyw7'); const result = await fetchBook(id); ͕͢͞ʹࠓ౓͸Αͦ͞͏ ʁ

Slide 27

Slide 27 text

const id = new UserId('ckyx6c8xc000009jm8acefv2v'); const result = await fetchBook(id); ͩΊͰ͢ 
 ͜Ε΋ίϯύΠϧ͕௨Δ

Slide 28

Slide 28 text

5ZQF4DSJQU͸4USVDUVSBM5ZQJOHͰ͋Δ w 5ZQF4DSJQU͸ܕͷผ໊Ͱ۠ผͨ͠Γɺ Ϋϥε໊͕ҟͳΔ͜ͱͰ۠ผ͢ΔͷͰ͸ͳ͘ 
 ৗʹ ʮϓϩύςΟͷߏ੒ʯ ʹΑͬͯܕͷ۠ผΛ͚ͭΔ w ͜͏͍ͬͨܕͷ൑ผํࣜΛ4USVDUVSBM5ZQJOHͱ͍͏ w ͜Ε͸υΩϡϝϯ τʹ΋هࡌ͞Ε͍ͯΔ 
 https://www.typescriptlang.org/docs/handbook/type-compatibility.html w ܕ໊ͷҧ͍͚ͩͰܕ͕ҟͳ͍ͬͯΔͱ൑ผ͢Δํࣜ͸/PNJOBM5ZQJOHͱ͍͏ w 5ZQF4DSJQU͸ͪ͜Βͷํࣜ͸࠾༻͍ͯ͠ͳ͍

Slide 29

Slide 29 text

ϓϩύςΟͷҰக class UserId { constructor(public readonly value: string) {} } class BookId { constructor(public readonly value: string) {} } valueͱ͍͏ ϓϩύςΟ͕ݸ͋ΔΫϥε 
 ͱ͍͏఺Ͱɺ ߏ଄͕Ұக͍ͯ͠Δ 
 Αͬͯ4USVDUVSBM5ZQJOHʹج͍ͮͯ͜ΕΒ͸ಉҰܕͱΈͳ͞ΕΔ

Slide 30

Slide 30 text

ϓϩύςΟ ΛҰகͤ͞ͳ͍Α ͏ʹ͢Δ class UserId { userId: unknown; constructor(public readonly value: string) {} } class BookId { bookId: unknown; constructor(public readonly value: string) {} } ͦΕͧΕͷΫϥεʹҟͳΔ໊લͷμϛʔϓϩύςΟ Λ଍͢͜ͱͰ 
 ߏ଄͕Ұக͠ͳ͍Α ͏ʹͨ͠

Slide 31

Slide 31 text

const id = new UserId('ckyx6c8xc000009jm8acefv2v'); const result = await fetchBook(id); Argument of type 'UserId' is not assignable to parameter of type 'BookId'. Property 'bookId' is missing in type 'UserId' but required in type 'BookId'.(2345) Τϥʔʹͳͬͯ ͘Εͨ

Slide 32

Slide 32 text

ʜͱ͍͏࿩͸೥ʹ͠· ͨ͠

Slide 33

Slide 33 text

ʜͱ͍͏࿩͸೥ʹ͠· ͨ͠ 5ZQF4DSJQUಋೖͰಘΒΕΔ ʮม͍͑ͯ͘༐ؾʯ 
 https://speakerdeck.com/okunokentaro/the-courage-to-change-by-typescript

Slide 34

Slide 34 text

Ϋϥε๏͸Կ͕Πέͯͳ͍͔ w 5ZQF4DSJQUͰΫϥεશൠΛ࢖͏ ͜ͱͷੋඇΛड़΂ΔΘ͚Ͱ͸ͳ͍ w /PNJOBM5ZQJOH ʮͬΆ͍ʯ ͜ͱΛ͢ΔͨΊʹΫϥεΛಋೖ͢Δͷ͸Πέͯͳ͍ w ͳ͔ͥ ʁ

Slide 35

Slide 35 text

const id = new UserId('ckyx6c8xc000009jm8acefv2v'); await fetch(`https://something/api/users/${id}`); 5FNQMBUFMJUFSBMTʹͯ ͜ͷΑ ͏ʹύεʹ*%ؚ͕·ΕΔΑ ͏ͳ63-͸Α ͘ ͋Δ 
 ݁Ռ͸ ʁ

Slide 36

Slide 36 text

https://something/api/users/[object Object] 👆 👆 👆 👆 👇 👇👇 👇 👇

Slide 37

Slide 37 text

Ϧςϥϧͱ ͯ͠ѻ͑ͳ͍ w new UserId()͍ͯ͠ΔͨΊɺ ݁Ռ͸typeof id === 'object'ͱͳΔ w จࣈྻϦςϥϧͰ͸ͳ͘ɺ valueΛϓϩύςΟ ͱ ͯ࣋ͭ͠ΦϒδΣΫ τʹͳΔ w toString()Λ࣮૷͢Ε͹[object Object]͸ճආͰ͖Δ͕ 
 ͜ͷख๏Λࢼ͢ࡍʹຖճͦͷ࣮૷͕ඞཁ w μϛʔϓϩύςΟ Λੜ΍͞ͳ͖ΌࣝผͰ͖ͳ͍ͷ΋ඇຊ࣭త w Ϧςϥϧͷ··ѻ͑ͯɺ ͔ͭ/PNJOBM5ZQJOHͬΆ͍ܕ൑ผΛ͢Δʹ͸ʜ ʁ

Slide 38

Slide 38 text

ྩ࿨࠷৽൛

Slide 39

Slide 39 text

UZQF/PNJOBM w type NominalΛએݴ͢Δ w { __brand: U }ͱ͍͏ΦϒδΣΫ τͱͷަࠩܕ & ʹ͢Δ͜ͱͰϦςϥϧͷ·· ۠ผͰ͖ΔΑ ͏ʹ͢Δ w ਖ਼֬ͳग़య͸ൃݟͰ͖͍ͯͳ͍͕ෳ਺ͷจݙ͋Γ w #SBOEFE5ZQFTͱ΋ݺ͹Ε͍ͯΔ͕ 
 ʮ/PNJOBM5ZQJOHΛ࣮ݱ͍ͨ͠ͷ͔ͩΒʯ ͱ͍͏҆௚ͳ೔ຊਓతޠ๏Ͱ 
 ࠓճ͸Nominalͱͨ͠ export type Nominal = T & { __brand: U };

Slide 40

Slide 40 text

/PNJOBMܕΛ࢖ͬͯ࠶௅ઓ type Nominal = T & { __brand: U }; type BookId = Nominal; type UserId = Nominal; const id = 'ckyx6c8xc000009jm8acefv2v' as UserId; const result = await fetchBook(id); Argument of type 'UserId' is not assignable to parameter of type 'BookId'. Type 'UserId' is not assignable to type '{ __brand: "BookId"; }'. Types of property '__brand' are incompatible. Type '"UserId"' is not assignable to type '"BookId"'.(2345) ͪΌΜͱ۠ผͰ͖͍ͯΔ

Slide 41

Slide 41 text

type Nominal = T & { __brand: U }; type BookId = Nominal; type UserId = Nominal; const id = 'ckyx6c8xc000009jm8acefv2v' as UserId; const result = await fetchBook(id); ʮͰ΋BTېࢭͯ͠·ͤΜͰͨ͠ ʁ ʯ

Slide 42

Slide 42 text

લճͷ͓͞Β͍ https://speakerdeck.com/okunokentaro/techstand6 Ԟ໺ݡଠ࿠͸લճͷ5ZQF4DSJQUొஃʹͯBTΛېࢭ͍ͯͨ͠ ʂ ʂ ʂ

Slide 43

Slide 43 text

"TTFSUJPOGVODUJPOTΛ࢖͏ Α ͏ʹ͢Δ w ͜ͷख๏Ͱ͸ΧδϡΞϧʹas͕ग़͖ͯͯ͠·͍ ʮBTېࢭʯ ʹ൓ͯ͠͠· ͏ w ͦͷͨΊɺ Nominalܕͷ஋Ͱ͋Δͱ͍ࣔͨ͠৔߹͸ඞͣ 
 5ZQFQSFEJDBUFTɺ ·ͨ͸"TTFSUJPOGVODUJPOTΛ࢖͏ Α ͏ʹ͢Δ

Slide 44

Slide 44 text

BTTFSU4USJOH ؔ਺Λ࡞ͬͯΈΔ export function isString(v: unknown): v is string { return typeof v === 'string'; } export function assertString(v: unknown, target = ''): asserts v is string { if (!isString(v)) { throw new Error(`${target} should be string`.trim()); } } ౰ͨΓલͷΑ ͏ͳ಺༰ͷؔ਺͕ͩɺ 
 unknownܕΛstringܕͱΈͳͨ͢Ίʹ͸ɺ Θ͟Θ͟͜͏͍ͬͨؔ਺͕ඞཁ

Slide 45

Slide 45 text

/PNJOBMܕͱ"TTFSUJPOGVODUJPOTͷԠ༻ import { isString } from './assert-string'; import { Nominal } from './nominal'; type FilledString = Nominal; function isFilledString(v: unknown): v is string { return isString(v) && v !== ''; } function assertFilledString( v: unknown, target = '' ): asserts v is FilledString { if (!isFilledString(v)) { throw new Error(`${target} should be not empty string`.trim()); } } type FilledStringΛએݴ͠ɺ ඞۭͣจࣈྻͰ͸ͳ͍͜ͱΛද໌͢Δ

Slide 46

Slide 46 text

/PNJOBMܕͷෳ߹ import { FilledString, assertFilledString } from '../utils/filled-string'; import { Nominal } from '../utils/nominal'; export type UserId = Nominal; function assertUserId(v: unknown): asserts v is UserId { assertFilledString(v); } export function asUserId(v: unknown): UserId { assertUserId(v); return v; } ͞Βʹɺ FilledStringܕΛຬͨ͢൑ผՄೳܕͱͯ͠type UserIdΛએݴ͢Δɻ 
 ಉख๏Ͱએݴ͢Δtype BookIdͱίϯύΠ ϥ͕۠ผͰ͖Δ্ʹ 
 ۭจࣈؚ͕·Εಘͳ͍͜ͱ΋อূࡁΈ ʂ 
 ʢ"TTFSUJPOGVODUJPOʹ͸ࣗಈςε τͷ࣮ࢪ͕ඞਢʣ

Slide 47

Slide 47 text

Ԡ༻ͷՄೳੑ͸ແݶେ w ͜ͷख๏Λ׆༻͢Δͱ Ϧςϥϧͷදݱͷ෯͕޿͕Δ w ྫ͑͹ʜ w ۭจࣈྻΛېࢭ͢Δ6TFS*Eܕ w ಛఆͷਖ਼نදݱΛຬ͍ͨͯ͠ͳ͍ͱΤϥʔͱͳΔ'PSNFEܕ w খ਺஋΍/B/ΛೝΊͳ͍6OJY5JNFܕ w ۠ผՄೳͳ4FDPOEܕͱ.JMMJTFDPOEܕ w ͳͲͳͲ

Slide 48

Slide 48 text

ܕΞϊςʔγϣϯ͸ ʮܕ͔͠Θ͔Βͳ͍ʯ w fetchBook(id: string)Ͱ͸ 
 ͳΜΒ͔ͷจࣈྻΛҾ਺ʹͱΔ͜ͱ ͔͠Θ͔Βͳ͍ w ͦΕ͸UserId͔΋͠Εͳ͍͠ɺ ۭจࣈྻΛ౉ͯ͠΋͍͍͔΋͠Εͳ͍ w ܕΞϊςʔγϣϯ͔Β͸ ʮstringܕͷ஋Ͱ͋Δ͜ͱʯ Ҏ֎ͳʹ΋Θ͔Βͳ͍ w ೥લͷ+BWB4DSJQUˠ5ZQF4DSJQUͷ࣌୅͸ɺ ͜ΕͰ΋ཱ೿ͳ΋Μͩͬͨ

Slide 49

Slide 49 text

ܕΞϊςʔγϣϯʹؚΊΔ৘ใྔΛ૿΍͢ w type BookId = NominalΛએݴ͓ͯ͘͠ ͱ 
 fetchBook(id: BookId)ͱ͍͏γάωνϟ͔ΒಘΒΕΔ৘ใྔ͕ 
 fetchBook(id: string)ʹൺ΂͙ͯͬͱ૿͢ w ͦΕ͸UserIdͰ͸μϝͱΘ͔Δ w ͦΕ͸ۭจࣈྻͰ͸μϝͱΘ͔Δ w ࣮૷୲౰ऀʹޱ಄Ͱฉ͍ͯճΔඞཁ͕ͳ͍ w ͜ͷߟ͑ํ͸ܖ໿ϓϩάϥϛϯά ʢ%FTJHOCZ$POUSBDU %C$ͳͲͱ΋ʣ ͷ 
 ํ๏࿦ʹج͍͍ͮͯΔ w ಛʹࣄલ৚݅΍ෆม৚݅ʹ֘౰

Slide 50

Slide 50 text

த਎ͷ͋ΔΞϊςʔγϣϯ΁ w ཁ݅ΛܕΞϊςʔγϣϯʹؚΊΔʕʕཁ݅Ξϊςʔγϣϯʹ͢Δ͜ͱͰ 
 ߦͷίʔ υ͔ΒಡΈखʹ఻ΘΔҙਤ͕Α Γ๛͔ʹͳΔ w ͜Ε·Ͱͷ೥͸+BWB4DSJQU͔Β5ZQF4DSJQU΁ͷա౉ظͩͬͨ w ͜Ε͔Β͸ίʔ υ͕5ZQF4DSJQUͱͯ͠ɺ 5ZQF4DSJQUΒ͘͠੒ख़͍ͯ࣌͘͠୅ w 5ZQF4DSJQUͱ ͯ͠Α Γ๛͔ͳදݱΛ͢ΔͳΒ 
 த਎ͷͳ͍ΞϊςʔγϣϯΑ Γத਎ͷ͋ΔΞϊςʔγϣϯΛ৺͕͚ΔͱΑ͍

Slide 51

Slide 51 text

5IBOLZPV https://commons.wikimedia.org/wiki/File:IRS46_nasa.jpg