Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TypeScript CompilerAPI によるVuexの参照型生成

Takepepe
December 11, 2019

TypeScript CompilerAPI によるVuexの参照型生成

【PLAID × ラクスル】Vue.js for 2020

Takepepe

December 11, 2019
Tweet

More Decks by Takepepe

Other Decks in Technology

Transcript

  1. About Me ▪ Takefumi Yoshii / @Takepepe ▪ DeNA /

    DeSC Healthcare ▪ Frontend Engineer ▪ TypeScript Meetup JP member 2
  2. 1-4. 不自然なコードベース 単純な関数定義を思い出してみてください。 28 28 28 function getRank(amount?: number) {

    if (!amount) return null if (amount > 50) return 'A' return 'B' } const rank1 = getRank() const rank2 = getRank(50) const rank1: "A" | "B" | null const rank2: "A" | "B" | null inferred
  3. 1-4. 不自然なコードベース 推論器は実装内容を正しく捉え、型注釈なくして型情報は導かれます。 29 29 29 function getRank(amount?: number) {

    if (!amount) return null if (amount > 50) return 'A' return 'B' } const rank1 = getRank() const rank2 = getRank(50) const rank1: "A" | "B" | null const rank2: "A" | "B" | null inferred
  4. 1-4. 不自然なコードベース 私はこれを「TypeScriptのアイデンティティ」だと感じています。 30 30 30 function getRank(amount?: number) {

    if (!amount) return null if (amount > 50) return 'A' return 'B' } const rank1 = getRank() const rank2 = getRank(50) const rank1: "A" | "B" | null const rank2: "A" | "B" | null inferred
  5. 2-3. vuex-guardian code base 動画で紹介した コードベースを おさらいします。 44 44 44

    import { LocalContext } from 'vuex' type Context = LocalContext['counter'] export const mutations = { setCount(state: State, payload: { amount: number }) { state.count = payload.amount } } export const actions = { acyncSetCount(ctx: Context, payload: { amount: number }) { ctx.commit({ type: 'setCount', amount: payload.amount }) } }
  6. 2-3. vuex-guardian code base はじめに、LocalContext を import 。単一 Module の

    Context を特定します。 45 45 45 import { LocalContext } from 'vuex' type Context = LocalContext['counter'] // Module NameSpace export const mutations = { setCount(state: State, payload: { amount: number }) { state.count = payload.amount } } export const actions = { acyncSetCount(ctx: Context, payload: { amount: number }) { ctx.commit({ type: 'setCount', amount: payload.amount }) } }
  7. 2-3. vuex-guardian code base これを action 関数の 引数に型注釈します。 46 46

    46 import { LocalContext } from 'vuex' type Context = LocalContext['counter'] export const mutations = { setCount(state: State, payload: { amount: number }) { state.count = payload.amount } } export const actions = { acyncSetCount(ctx: Context, payload: { amount: number }) { ctx.commit({ type: 'setCount', amount: payload.amount }) } }
  8. 2-3. vuex-guardian code base これだけで、単一 Module 内の定義に対し、 型安全が約束されます。 47 47

    47 import { LocalContext } from 'vuex' type Context = LocalContext['counter'] export const mutations = { setCount(state: State, payload: { amount: number }) { state.count = payload.amount } } export const actions = { acyncSetCount(ctx: Context, payload: { amount: number }) { ctx.commit({ type: 'setCount', amount: payload.amount }) } }
  9. 2-3. vuex-guardian code base mutation 関数引数を 変更したら、 コンパイルエラーを得る ことが出来ます。 48

    48 48 import { LocalContext } from 'vuex' type Context = LocalContext['counter'] export const mutations = { setCount(state: State, payload: { amount: string }) { state.count = payload.amount } } export const actions = { acyncSetCount(ctx: Context, payload: { amount: number }) { ctx.commit({ type: 'setCount', amount: payload.amount }) } }
  10. 2-3. vuex-guardian code base 関数名の変更も、 すぐさま検知されます。 49 49 49 import

    { LocalContext } from 'vuex' type Context = LocalContext['counter'] export const mutations = { setValue(state: State, payload: { amount: number }) { state.count = payload.amount } } export const actions = { acyncSetCount(ctx: Context, payload: { amount: number }) { ctx.commit({ type: 'setCount', amount: payload.amount }) } }
  11. 2-3. vuex-guardian code base 定義することは、 これだけです。 実装を型定義の 一次ソースとしています。 50 50

    50 import { LocalContext } from 'vuex' type Context = LocalContext['counter'] export const mutations = { setCount(state: State, payload: { amount: number }) { state.count = payload.amount } } export const actions = { acyncSetCount(ctx: Context, payload: { amount: number }) { ctx.commit({ type: 'setCount', amount: payload.amount }) } }
  12. 2-3. vuex-guardian code base 'vuex' パッケージには、 全 Store 実装の「型」 が格納されています。

    51 51 51 import { LocalContext } from 'vuex' type Context = LocalContext['counter'] export const mutations = { setCount(state: State, payload: { amount: number }) { state.count = payload.amount } } export const actions = { acyncSetCount(ctx: Context, payload: { amount: number }) { ctx.commit({ type: 'setCount', amount: payload.amount }) } }
  13. 3-2. 型の部分抽出 次の「A2」の様な補助型を利用すると、第二引数型が抽出できます。 64 64 64 type A2<T> = T

    extends (a1: any, a2: infer I) => any ? I : never type T = typeof addTodo type U = A2<typeof addTodo> type T = (state: State, todo: Todo) => void type U = Todo inferred
  14. 3-2. 型の部分抽出 ここが「Type Inference in conditional types」です。 65 65 65

    type A2<T> = T extends (a1: any, a2: infer I) => any ? I : never type T = typeof addTodo type U = A2<typeof addTodo> type T = (state: State, todo: Todo) => void type U = Todo inferred
  15. 3-2. 型の部分抽出 typeof 型クエリーを使うことで、実装から型を抽出できます。 66 66 66 type A2<T> =

    T extends (a1: any, a2: infer I) => any ? I : never type T = typeof addTodo type U = A2<typeof addTodo> type T = (state: State, todo: Todo) => void type U = Todo inferred
  16. 3-2. 型の部分抽出 「addTodo」という、さきほどの関数定義を参照しています。 67 67 67 type A2<T> = T

    extends (a1: any, a2: infer I) => any ? I : never type T = typeof addTodo type U = A2<typeof addTodo> type T = (state: State, todo: Todo) => void type U = Todo inferred
  17. 3-2. 型の部分抽出 それは新しい型として定義され、参照を継続します。 68 68 68 type A2<T> = T

    extends (a1: any, a2: infer I) => any ? I : never type T = typeof addTodo type U = A2<typeof addTodo> type T = (state: State, todo: Todo) => void type U = Todo inferred
  18. 3-2. 型の部分抽出 実際に mutation 関数から型を抽出してみます。 69 69 69 export const

    mutations = { addTodo(state: State, payload: { todo: Todo }) { state.todos.push(payload.todo) } } type T = A2<typeof mutations['addTodo']> type T = { todo: Todo }
  19. 3-2. 型の部分抽出 型定義は JavaScript と同じように、添字参照が出来ます。 70 70 70 export const

    mutations = { addTodo(state: State, payload: { todo: Todo }) { state.todos.push(payload.todo) } } type T = A2<typeof mutations['addTodo']> type T = { todo: Todo }
  20. 3-2. 型の部分抽出 typeof 型クエリーと、さきほどの「A2」型を併用すると… 71 71 71 export const mutations

    = { addTodo(state: State, payload: { todo: Todo }) { state.todos.push(payload.todo) } } type T = A2<typeof mutations['addTodo']> type T = { todo: Todo }
  21. 3-2. 型の部分抽出 これはまさに、store.commit に必要な payload 型です。 72 72 72 export

    const mutations = { addTodo(state: State, payload: { todo: Todo }) { state.todos.push(payload.todo) } } type T = A2<typeof mutations['addTodo']> type T = { todo: Todo }
  22. 3-2. 型の部分抽出 次に、getter 関数を見ていきましょう。 73 73 73 export const getters

    = { doneCount(state: State) { return state.todos.filter(todo => todo.done).length } } type T = ReturnType<typeof getters['doneCount']> type T = number
  23. 3-2. 型の部分抽出 この関数は、外部からは値として見えます。 74 74 74 export const getters =

    { doneCount(state: State) { return state.todos.filter(todo => todo.done).length } } type T = ReturnType<typeof getters['doneCount']> type T = number this.$store.getters['todos/doneCount']
  24. 3-2. 型の部分抽出 これは、戻り型で表現することができます。 75 75 75 export const getters =

    { doneCount(state: State) { return state.todos.filter(todo => todo.done).length } } type T = ReturnType<typeof getters['doneCount']> type T = number
  25. 3-2. 型の部分抽出 ビルトインユーティリティ型の「ReturnType」を利用すれば… 76 76 76 export const getters =

    { doneCount(state: State) { return state.todos.filter(todo => todo.done).length } } type T = ReturnType<typeof getters['doneCount']> type T = number
  26. 3-2. 型の部分抽出 値に相当する型を抽出することができました。 77 77 77 export const getters =

    { doneCount(state: State) { return state.todos.filter(todo => todo.done).length } } type T = ReturnType<typeof getters['doneCount']> type T = number
  27. 3-3. 型の文字列参照 参照型は「一つの型定義」に集約します。 84 84 84 interface RootGetters { 'todos/doneItems':

    LocalGetters['todos']['doneItems'] 'todos/visibleItems': LocalGetters['todos']['visibleItems'] 'todos/doneCount': LocalGetters['todos']['doneCount'] }
  28. 3-3. 型の文字列参照 全ての参照は、集約されたこの型を利用します。 85 85 85 interface RootGetters { 'todos/doneItems':

    LocalGetters['todos']['doneItems'] 'todos/visibleItems': LocalGetters['todos']['visibleItems'] 'todos/doneCount': LocalGetters['todos']['doneCount'] }
  29. 3-4. 宣言空間の活用 集約した参照型は、 'vuex' 名前空間に対し Ambient Module 宣言。 87 87

    87 import 'vuex' declare module 'vuex' { // Ambient Module Declaration interface RootGetters { 'counter/double': LocalGetters['counter']['double'] 'counter/expo': LocalGetters['counter']['expo'] 'todos/todosCount': LocalGetters['todos']['todosCount'] 'todos/doneCount': LocalGetters['todos']['doneCount'] } }
  30. 3-4. 宣言空間の活用 集約した参照型は、 'vuex' 名前空間に対し Ambient Module 宣言。 88 88

    88 import 'vuex' declare module 'vuex' { interface RootGetters { 'counter/double': LocalGetters['counter']['double'] 'counter/expo': LocalGetters['counter']['expo'] 'todos/todosCount': LocalGetters['todos']['todosCount'] 'todos/doneCount': LocalGetters['todos']['doneCount'] } }
  31. 3-4. 宣言空間の活用 Ambient Module 宣言 によりその型定義は、 まるでライブラリに本来 備わったものである かの様に振る舞います。 89

    89 89 import 'vuex' declare module 'vuex' { interface RootGetters { 'counter/double': LocalGetters['counter']['double'] 'counter/expo': LocalGetters['counter']['expo'] 'todos/todosCount': LocalGetters['todos']['todosCount'] 'todos/doneCount': LocalGetters['todos']['doneCount'] } }
  32. 3-4. 宣言空間の活用 vuex-guardian は、 より強固に定義された 「StrictStore型」を 持ちます。 90 90 90

    import 'vuex' declare module 'vuex' { interface StrictStore extends Store<RootState> { getters: RootGetters commit: StrictCommit<MutationTypes> dispatch: StrictDispatch<ActionTypes> } }
  33. 3-4. 宣言空間の活用 「StrictStore型」は このように宣言空間に 宣言された集約型を 参照します。 91 91 91 import

    'vuex' declare module 'vuex' { interface StrictStore extends Store<RootState> { getters: RootGetters commit: StrictCommit<MutationTypes> dispatch: StrictDispatch<ActionTypes> } }
  34. 3-4. 宣言空間の活用 この「StrictStore型」が もつ commit / dispatch について、もう少し 深掘りしていきます。 92

    92 92 import 'vuex' declare module 'vuex' { interface StrictStore extends Store<RootState> { getters: RootGetters commit: StrictCommit<MutationTypes> dispatch: StrictDispatch<ActionTypes> } }
  35. 3-5. 文字列参照と Lookup $store.dispatch について確認していきましょう。 94 94 94 interface ActionTypes

    { 'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] } interface StrictDispatch<A> { <T extends keyof A>(type: T, payload?: A[T], options?: DispatchOptions): Promise<void> } this.$store.dispatch('todos/acyncDoneTodo', { id: this.data.id })
  36. 3-5. 文字列参照と Lookup 従来、文字列参照による型の特定は出来ませんでした。 95 95 95 interface ActionTypes {

    'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] } interface StrictDispatch<A> { <T extends keyof A>(type: T, payload?: A[T], options?: DispatchOptions): Promise<void> } this.$store.dispatch('todos/acyncDoneTodo', { id: this.data.id })
  37. 3-5. 文字列参照と Lookup 参照型が格納されたプロパティキーは、文字列です。 96 96 96 interface ActionTypes {

    'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] } interface StrictDispatch<A> { <T extends keyof A>(type: T, payload?: A[T], options?: DispatchOptions): Promise<void> } this.$store.dispatch('todos/acyncDoneTodo', { id: this.data.id })
  38. 3-5. 文字列参照と Lookup 格納されている参照型は、payload型に相当します。 97 97 97 interface ActionTypes {

    'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] } interface StrictDispatch<A> { <T extends keyof A>(type: T, payload?: A[T], options?: DispatchOptions): Promise<void> } this.$store.dispatch('todos/acyncDoneTodo', { id: this.data.id })
  39. 3-5. 文字列参照と Lookup 実は、これは強推論のために用意されたものです。 98 98 98 interface ActionTypes {

    'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] } interface StrictDispatch<A> { <T extends keyof A>(type: T, payload?: A[T], options?: DispatchOptions): Promise<void> } this.$store.dispatch('todos/acyncDoneTodo', { id: this.data.id })
  40. 3-5. 文字列参照と Lookup 関数は与えられた文字列から、型の Lookup が可能です。 99 99 99 interface

    ActionTypes { 'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] } interface StrictDispatch<A> { <T extends keyof A>(type: T, payload?: A[T], options?: DispatchOptions): Promise<void> } this.$store.dispatch('todos/acyncDoneTodo', { id: this.data.id })
  41. 3-5. 文字列参照と Lookup 第一引数(ActionType文字列)が与えられた時… 100 100 100 interface ActionTypes {

    'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] } interface StrictDispatch<A> { <T extends keyof A>(type: T, payload?: A[T], options?: DispatchOptions): Promise<void> } this.$store.dispatch('todos/acyncDoneTodo', { id: this.data.id })
  42. 3-5. 文字列参照と Lookup それに対応する型が確定します。 101 101 101 interface ActionTypes {

    'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] } interface StrictDispatch<A> { <T extends keyof A>(type: T, payload?: A[T], options?: DispatchOptions): Promise<void> } this.$store.dispatch('todos/acyncDoneTodo', { id: this.data.id })
  43. 3-5. 文字列参照と Lookup もう一度。第一引数(ActionType文字列)が与えられた時… 102 102 102 interface ActionTypes {

    'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] } interface StrictDispatch<A> { <T extends keyof A>(type: T, payload?: A[T], options?: DispatchOptions): Promise<void> } this.$store.dispatch('todos/acyncDoneTodo', { id: this.data.id })
  44. 3-5. 文字列参照と Lookup それに対応する型が確定します。 103 103 103 interface ActionTypes {

    'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] } interface StrictDispatch<A> { <T extends keyof A>(type: T, payload?: A[T], options?: DispatchOptions): Promise<void> } this.$store.dispatch('todos/acyncDoneTodo', { id: this.data.id })
  45. 3-5. 文字列参照と Lookup この機能により、コードに型情報がなくとも、型安全が担保されます。 104 104 104 interface ActionTypes {

    'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] } interface StrictDispatch<A> { <T extends keyof A>(type: T, payload?: A[T], options?: DispatchOptions): Promise<void> } this.$store.dispatch('todos/acyncDoneTodo', { id: this.data.id })
  46. 3-5. 文字列参照と Lookup JavaScript コードと「同じ」であっても、型情報はついてまわります。 105 105 105 interface ActionTypes

    { 'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] } interface StrictDispatch<A> { <T extends keyof A>(type: T, payload?: A[T], options?: DispatchOptions): Promise<void> } this.$store.dispatch('todos/acyncDoneTodo', { id: this.data.id })
  47. 4-1. 創出の型パズル「Compiler API」 こうして全ての参照型を集約し 強化された「StrictStore型」は、 SFC の $store に適用されます。 108

    108 108 interface LocalGetters { todos: { todosCount:ReturnType<Modules['todos']['getters']['todosCount']> doneCount: ReturnType<Modules['todos']['getters']['doneCount']> } } interface RootGetters { 'todos/todosCount': LocalGetters['todos']['todosCount'] 'todos/doneCount': LocalGetters['todos']['doneCount'] } interface MutationTypes { 'todos/addTodo': LocalMutationTypes['todos']['addTodo'] 'todos/doneTodo': LocalMutationTypes['todos']['doneTodo'] } interface ActionTypes { 'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] }
  48. 4-1. 創出の型パズル「Compiler API」 そのため SFC であっても、 Vuex における変化を、 コンパイルエラーとして 検出することが出来ます。

    109 109 109 interface LocalGetters { todos: { todosCount:ReturnType<Modules['todos']['getters']['todosCount']> doneCount: ReturnType<Modules['todos']['getters']['doneCount']> } } interface RootGetters { 'todos/todosCount': LocalGetters['todos']['todosCount'] 'todos/doneCount': LocalGetters['todos']['doneCount'] } interface MutationTypes { 'todos/addTodo': LocalMutationTypes['todos']['addTodo'] 'todos/doneTodo': LocalMutationTypes['todos']['doneTodo'] } interface ActionTypes { 'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] }
  49. 4-1. 創出の型パズル「Compiler API」 しかし、参照型の量は膨大です。 手動でこの型を定義していくのは、 あまりにも骨が折れます。 110 110 110 interface

    LocalGetters { todos: { todosCount:ReturnType<Modules['todos']['getters']['todosCount']> doneCount: ReturnType<Modules['todos']['getters']['doneCount']> } } interface RootGetters { 'todos/todosCount': LocalGetters['todos']['todosCount'] 'todos/doneCount': LocalGetters['todos']['doneCount'] } interface MutationTypes { 'todos/addTodo': LocalMutationTypes['todos']['addTodo'] 'todos/doneTodo': LocalMutationTypes['todos']['doneTodo'] } interface ActionTypes { 'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] }
  50. 4-1. 創出の型パズル「Compiler API」 リファクタリングしようものなら、 多大な修正を強いられます。 これは求めたものではありません。 111 111 111 interface

    LocalGetters { todos: { todosCount:ReturnType<Modules['todos']['getters']['todosCount']> doneCount: ReturnType<Modules['todos']['getters']['doneCount']> } } interface RootGetters { 'todos/todosCount': LocalGetters['todos']['todosCount'] 'todos/doneCount': LocalGetters['todos']['doneCount'] } interface MutationTypes { 'todos/addTodo': LocalMutationTypes['todos']['addTodo'] 'todos/doneTodo': LocalMutationTypes['todos']['doneTodo'] } interface ActionTypes { 'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] }
  51. 4-1. 創出の型パズル「Compiler API」 この「参照型」を自動生成 するために最適な機能が 「CompilerAPI」です。 112 112 112 interface

    LocalGetters { todos: { todosCount:ReturnType<Modules['todos']['getters']['todosCount']> doneCount: ReturnType<Modules['todos']['getters']['doneCount']> } } interface RootGetters { 'todos/todosCount': LocalGetters['todos']['todosCount'] 'todos/doneCount': LocalGetters['todos']['doneCount'] } interface MutationTypes { 'todos/addTodo': LocalMutationTypes['todos']['addTodo'] 'todos/doneTodo': LocalMutationTypes['todos']['doneTodo'] } interface ActionTypes { 'todos/acyncAddTodo': LocalActionTypes['todos']['acyncAddTodo'] 'todos/acyncDoneTodo': LocalActionTypes['todos']['acyncDoneTodo'] }
  52. 4-3. tsc と CompilerAPI の概要 その AST をもって、指定された target バージョンの

    JavaScript に トランスパイルされ、出力に至ります。 121 121 121 emitFiles
  53. 4-4. AST を辿る Nuxt.js の Vuex Module Mode は、 規約に従います。

    Node.js のファイルシステムが 開発をサポートします。 133 133 133 store ├── counter │ └── index.ts ├── index.ts └── todos └── index.ts
  54. 4-4. AST を辿る Vuex Module Mode ならではの、 このファイルツリー構造規約が、 今回のアイディアに合致しました。 134

    134 134 store ├── counter │ └── index.ts ├── index.ts └── todos └── index.ts "todos/todosCount": ReturnType<Modules["todos"]["getters"]["todosCount"]>
  55. 4-4. AST を辿る プロパティキーに相当する文字列は、 ファイルパスから辿ることが できるためです。 135 135 135 "todos/todosCount":

    ReturnType<Modules["todos"]["getters"]["todosCount"]> store ├── counter │ └── index.ts ├── index.ts └── todos └── index.ts
  56. 4-4. AST を辿る 型推論では不可能だった、 文字列型の合成は、 Node.js にとっては容易いことです。 136 136 136

    "todos/todosCount": ReturnType<Modules["todos"]["getters"]["todosCount"]> store ├── counter │ └── index.ts ├── index.ts └── todos └── index.ts
  57. 4-4. AST を辿る AST から得た関数名は、 この様に文字列合成され、 参照型の一部を担います。 137 137 137

    "todos/todosCount": ReturnType<Modules["todos"]["getters"]["todosCount"]> store ├── counter │ └── index.ts ├── index.ts └── todos └── index.ts
  58. 4-4. AST を辿る 文字列型は、TypeScript の強推論に はなくてはならないものです。 それは、Lookup を可能にします。 138 138

    138 "todos/todosCount": ReturnType<Modules["todos"]["getters"]["todosCount"]> store ├── counter │ └── index.ts ├── index.ts └── todos └── index.ts
  59. 4-4. AST を辿る 文字列型を自由に創出可能 ということは、大きな可能性を 意味します。 139 139 139 "todos/todosCount":

    ReturnType<Modules["todos"]["getters"]["todosCount"]> store ├── counter │ └── index.ts ├── index.ts └── todos └── index.ts
  60. 4-5. AST を創る CompilerAPI は、その SRC コードを パースするに留まりません。 ASTを構築するための、 ファクトリ関数群があります。

    141 141 141 import 'vuex' ts.createImportDeclaration( undefined, undefined, undefined, ts.createStringLiteral('vuex') )
  61. 4-5. AST を創る たとえば、この様な SRC コードを生成したい場合。 142 142 142 import

    'vuex' ts.createImportDeclaration( undefined, undefined, undefined, ts.createStringLiteral('vuex') )
  62. 4-5. AST を創る これに相当する AST は、 TypeScript から提供されている このファクトリ関数をもって 創ることができます。

    143 143 143 import 'vuex' ts.createImportDeclaration( undefined, undefined, undefined, ts.createStringLiteral('vuex') )
  63. 4-5. AST を創る ファクトリ関数に変数を与えれば、 任意の SRC コードを 創ることができます。 144 144

    144 import 'vuex' ts.createImportDeclaration( undefined, undefined, undefined, ts.createStringLiteral('vuex') )
  64. 4-5. AST を創る たとえば、この様な SRC コードを生成したい場合。 145 145 145 import

    * as Module from '/path/to/module' ts.createImportDeclaration( undefined, undefined, ts.createImportClause( undefined, ts.createNamespaceImport( ts.createIdentifier('Module') ) ), ts.createStringLiteral('/path/to/module') )
  65. 4-5. AST を創る これに相当する AST は、 TypeScript から提供されている このファクトリ関数をもって 創ることができます。

    146 146 146 import * as Module from '/path/to/module' ts.createImportDeclaration( undefined, undefined, ts.createImportClause( undefined, ts.createNamespaceImport( ts.createIdentifier('Module') ) ), ts.createStringLiteral('/path/to/module') )
  66. 4-5. AST を創る ファクトリ関数に変数を与えれば、 任意の SRC コードを 創ることができます。 147 147

    147 import * as Module from '/path/to/module' ts.createImportDeclaration( undefined, undefined, ts.createImportClause( undefined, ts.createNamespaceImport( ts.createIdentifier('Module') ) ), ts.createStringLiteral('/path/to/module') )
  67. 4-5. AST を創る AST ファクトリ関数を組み合わせ、 目的に沿った型定義を 創り出すことができます。 148 148 148

    import * as Module from '/path/to/module' ts.createImportDeclaration( undefined, undefined, ts.createImportClause( undefined, ts.createNamespaceImport( ts.createIdentifier('Module') ) ), ts.createStringLiteral('/path/to/module') )