Created
February 22, 2018 10:10
-
-
Save wonderful-panda/4f246b527cb643175d224bf974eb7f25 to your computer and use it in GitHub Desktop.
Conditional TypeでVuexのnamespacedモジュールを型安全にできないか考えてみるテスト
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// モジュールをマークするためのマーカーインターフェイス | |
interface Module { | |
_module_: 1 | |
} | |
// テスト用インターフェイス | |
interface Test { | |
foo: string; | |
bar: number; | |
baz: { | |
_module_: 1; | |
hoge: { // not module | |
a: string; | |
b: number; | |
}; | |
fuga: { | |
_module_: 1; | |
piyo: string; | |
a: { | |
_module_: 1; | |
x: {}; | |
}, | |
b: { // not module | |
x: {}; | |
} | |
} | |
} | |
} | |
// ValueOf<{ xxx: X, yyy: Y }> == X | Y | |
type ValueOf<T> = T[keyof T]; | |
// PropPaths<Test> == | |
// "foo" | "bar" | ["foo"] | ["bar"] | ["baz", "hoge"] | | |
// ["baz", "fuga", "piyo"] | ["baz", "fuga", "a", "x"] | ["baz", "fuga", "b"] | |
type PropPaths< | |
T, | |
K1 extends string = never, | |
K2 extends string = never, | |
K3 extends string = never, | |
K4 extends string = never | |
> = ValueOf<{ | |
[K in Exclude<keyof T, "_module_">]: ( | |
T[K] extends Module ? ( | |
K1 extends never ? PropPaths<T[K], K> : | |
K2 extends never ? PropPaths<T[K], K1, K> : | |
K3 extends never ? PropPaths<T[K], K1, K2, K> : | |
K4 extends never ? PropPaths<T[K], K1, K2, K3, K> : | |
never | |
) : ( | |
K1 extends never ? K | [K] : | |
K2 extends never ? [K1, K] : | |
K3 extends never ? [K1, K2, K] : | |
K4 extends never ? [K1, K2, K3, K] : | |
[K1, K2, K3, K4, K] | |
) | |
) | |
}>; | |
type PropTypeStrict< | |
T, | |
K1 extends keyof T, | |
K2 extends keyof T[K1] = never, | |
K3 extends keyof T[K1][K2] = never, | |
K4 extends keyof T[K1][K2][K3] = never, | |
K5 extends keyof T[K1][K2][K3][K4] = never | |
> = ( | |
K2 extends never ? T[K1] : | |
K3 extends never ? T[K1][K2] : | |
K4 extends never ? T[K1][K2][K3] : | |
K5 extends never ? T[K1][K2][K3][K4] : | |
T[K1][K2][K3][K4][K5] | |
); | |
// PropTypeTuple<Test, "baz", "hoge"> == { a: string, b: number } | |
type PropType<T, K1, K2 = void, K3 = void, K4 = void, K5 = void> = ( | |
K1 extends keyof T | |
? K2 extends keyof T[K1] | |
? K3 extends keyof T[K1][K2] | |
? K4 extends keyof T[K1][K2][K3] | |
? K5 extends keyof T[K1][K2][K3][K4] | |
? T[K1][K2][K3][K4][K5] | |
: K5 extends void ? T[K1][K2][K3][K4] : never | |
: K4 extends void ? T[K1][K2][K3] : never | |
: K3 extends void ? T[K1][K2] : never | |
: K2 extends void ? T[K1] : never | |
: never | |
); | |
// PropTypeTuple<Test, ["baz", "hoge"]> == { a: string, b: number } | |
type PropTypeTuple<T, Keys> = ( | |
Keys extends string ? PropType<T, Keys> : | |
Keys extends [infer K1] ? PropType<T, K1> : | |
Keys extends [infer K1, infer K2] ? PropType<T, K1, K2> : | |
Keys extends [infer K1, infer K2, infer K3] ? PropType<T, K1, K2, K3> : | |
Keys extends [infer K1, infer K2, infer K3, infer K4] ? PropType<T, K1, K2, K3, K4> : | |
Keys extends [infer K1, infer K2, infer K3, infer K4, infer K5] ? PropType<T, K1, K2, K3, K4, K5> : | |
never | |
); | |
type X = PropPaths<Test>; | |
// "foo" | "bar" | ["foo"] | ["bar"] | ["baz", "hoge"] | | |
// ["baz", "fuga", "piyo"] | ["baz", "fuga", "a", "x"] | ["baz", "fuga", "b"] | |
/** | |
* ここから本題 | |
*/ | |
// dispatchメソッドの型 | |
type Dispatch<T> = { | |
<X extends PropPaths<T>>(key: X, payload: PropTypeTuple<T, X>): void; | |
} | |
let dispatch: Dispatch<Test> = {} as any; | |
// OK | |
dispatch("foo", "test"); | |
dispatch(["foo"], "test"); | |
dispatch("bar", 1); | |
dispatch(["baz", "hoge"], { a: "1", b: 1 }); | |
dispatch(["baz", "fuga", "b"], { x: { aaa: true } }); | |
// NG | |
dispatch("fooo", "test"); // 存在しないpropname | |
dispatch("foo", 1); // 型の不一致 | |
dispatch(["baz", "hoge"], { a: 0, b: 1 }); // 型の不一致 | |
dispatch(["baz", "hage"], {}); // 存在しないproppath | |
dispatch(["baz", "fuga", "a"], { x: { aaa: true } }); // baz/huge/aは属性ではなくてモジュール |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment