From 11e4385368d1cd4bc79fabf5a06ccd86c0d7e29e Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 27 Jul 2020 22:41:46 +0800 Subject: [PATCH] feat: #12 --- README.md | 2 +- README.zh-CN.md | 2 +- .../12-medium-chainable-options/README.md | 32 +++++++++++++++++++ .../README.zh-CN.md | 32 +++++++++++++++++++ .../12-medium-chainable-options/info.yml | 8 +++++ .../info.zh-CN.yml | 1 + .../12-medium-chainable-options/template.ts | 4 +++ .../12-medium-chainable-options/test-cases.ts | 21 ++++++++++++ utils/index.d.ts | 2 ++ 9 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 questions/12-medium-chainable-options/README.md create mode 100644 questions/12-medium-chainable-options/README.zh-CN.md create mode 100644 questions/12-medium-chainable-options/info.yml create mode 100644 questions/12-medium-chainable-options/info.zh-CN.yml create mode 100644 questions/12-medium-chainable-options/template.ts create mode 100644 questions/12-medium-chainable-options/test-cases.ts diff --git a/README.md b/README.md index 950d7d48..bded6ae8 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ English | 简体中文 > Click the following badges to see detail of the challenges! -
#4・Pick<T, K> #7・Readonly<T>


#2・Get Return Type #3・Omit<T, K> #8・Readonly 2 #9・Deep Readonly #10・Tuple to Union


#5・Get Readonly Keys


#6・Simple Vue
By Tags
#application#6・Simple Vue
#built-in#4・Pick<T, K> #7・Readonly<T> #2・Get Return Type #3・Omit<T, K>
#deep#9・Deep Readonly
#infer#2・Get Return Type #10・Tuple to Union
#object-keys#7・Readonly<T> #8・Readonly 2 #9・Deep Readonly #5・Get Readonly Keys
#readonly#7・Readonly<T> #8・Readonly 2 #9・Deep Readonly
#this#6・Simple Vue
#tuple#10・Tuple to Union
#union#4・Pick<T, K> #3・Omit<T, K> #10・Tuple to Union
#utils#5・Get Readonly Keys
+
#4・Pick<T, K> #7・Readonly<T>


#2・Get Return Type #3・Omit<T, K> #8・Readonly 2 #9・Deep Readonly #10・Tuple to Union #12・Chainable Options


#5・Get Readonly Keys


#6・Simple Vue
By Tags
#application#12・Chainable Options #6・Simple Vue
#built-in#4・Pick<T, K> #7・Readonly<T> #2・Get Return Type #3・Omit<T, K>
#deep#9・Deep Readonly
#infer#2・Get Return Type #10・Tuple to Union
#object-keys#7・Readonly<T> #8・Readonly 2 #9・Deep Readonly #5・Get Readonly Keys
#readonly#7・Readonly<T> #8・Readonly 2 #9・Deep Readonly
#this#6・Simple Vue
#tuple#10・Tuple to Union
#union#4・Pick<T, K> #3・Omit<T, K> #10・Tuple to Union
#utils#5・Get Readonly Keys
## Recommended Readings diff --git a/README.zh-CN.md b/README.zh-CN.md index 444ea191..57ab4025 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -28,7 +28,7 @@ TypeScript 类型体操姿势合集 > 点击下方徽章查看题目内容 -
#4・实现 Pick<T, K> #7・实现 Readonly<T>


#2・获取函数返回类型 #3・实现 Omit<T, K> #8・Readonly 2 #9・深度 Readonly #10・元组转合集


#5・获取只读字段


#6・简单的 Vue 类型
By Tags
+
#4・实现 Pick<T, K> #7・实现 Readonly<T>


#2・获取函数返回类型 #3・实现 Omit<T, K> #8・Readonly 2 #9・深度 Readonly #10・元组转合集 #12・可串联构造器


#5・获取只读字段


#6・简单的 Vue 类型
By Tags
## 推荐读物 diff --git a/questions/12-medium-chainable-options/README.md b/questions/12-medium-chainable-options/README.md new file mode 100644 index 00000000..5b394a71 --- /dev/null +++ b/questions/12-medium-chainable-options/README.md @@ -0,0 +1,32 @@ +

Chainable Options medium #application

by Anthony Fu @antfu

Take the Challenge

+ +Chainable options are commonly used in Javascript. But when we switch to Typescript, can you properly type it? + +In this challenge, you need to type an object or a class - whatever you like - to provide two function `option(key, value)` and `get()`. In `option`, you can extend the the current config type by the given key and value. We should about to access the final result via `get`. + +For example + +```ts +declare const config: Chainable + +const result = config + .option('foo', 123) + .option('name', 'type-challenges') + .option('bar', { value: 'Hello World' }) + .get() + +// expect the type of result to be: +interface Result { + foo: number + name: string + bar: { + value: string + } +} +``` + +You don't need to write any js/ts logic to handle the problem - just in type level. + +You can assume that `key` only accept `string` and the `value` and be anything - just leave it as-is. Same `key` won't be passed twice. + +Back Check out Solutions Share your Solutions \ No newline at end of file diff --git a/questions/12-medium-chainable-options/README.zh-CN.md b/questions/12-medium-chainable-options/README.zh-CN.md new file mode 100644 index 00000000..eabbf2cf --- /dev/null +++ b/questions/12-medium-chainable-options/README.zh-CN.md @@ -0,0 +1,32 @@ +

可串联构造器 中等

by Anthony Fu @antfu

接受挑战

+ +在 JavaScript 中我们很常会使用可串联(Chainable/Pipeline)的函数构造一个对象,但在 TypeScript 中,你能合理的给他附上类型吗? + +在这个挑战中,你可以使用任意你喜欢的方式实现这个类型 - Interface, Type 或 Class 都行。你需要提供两个函数 `option(key, value)` 和 `get()`。在 `option` 中你需要使用提供的 key 和 value 扩展当前的对象类型,通过 `get` 获取最终结果。 + +例如 + +```ts +declare const config: Chainable + +const result = config + .option('foo', 123) + .option('name', 'type-challenges') + .option('bar', { value: 'Hello World' }) + .get() + +// 期望 result 的类型是: +interface Result { + foo: number + name: string + bar: { + value: string + } +} +``` + +你只需要在类型层面实现这个功能 - 不需要实现任何 TS/JS 的实际逻辑。 + +你可以假设 `key` 只接受字符串而 `value` 接受任何类型,你只需要暴露它传递的类型而不需要进行任何处理。同样的 `key` 只会被使用一次。 + +返回首页 查看解答 分享你的解答 \ No newline at end of file diff --git a/questions/12-medium-chainable-options/info.yml b/questions/12-medium-chainable-options/info.yml new file mode 100644 index 00000000..2ff6b7ae --- /dev/null +++ b/questions/12-medium-chainable-options/info.yml @@ -0,0 +1,8 @@ +title: Chainable Options + +author: + name: Anthony Fu + email: hi@antfu.me + github: antfu + +tags: application \ No newline at end of file diff --git a/questions/12-medium-chainable-options/info.zh-CN.yml b/questions/12-medium-chainable-options/info.zh-CN.yml new file mode 100644 index 00000000..37f17160 --- /dev/null +++ b/questions/12-medium-chainable-options/info.zh-CN.yml @@ -0,0 +1 @@ +title: 可串联构造器 \ No newline at end of file diff --git a/questions/12-medium-chainable-options/template.ts b/questions/12-medium-chainable-options/template.ts new file mode 100644 index 00000000..a587b1c6 --- /dev/null +++ b/questions/12-medium-chainable-options/template.ts @@ -0,0 +1,4 @@ +type Chainable = { + option(key: string, value: any): any + get(): any +} diff --git a/questions/12-medium-chainable-options/test-cases.ts b/questions/12-medium-chainable-options/test-cases.ts new file mode 100644 index 00000000..48b6116a --- /dev/null +++ b/questions/12-medium-chainable-options/test-cases.ts @@ -0,0 +1,21 @@ +import { Equal, Expect } from '@type-challenges/utils' + +declare const a: Chainable + +const result = a + .option('foo', 123) + .option('bar', { value: 'Hello World' }) + .option('name', 'type-challenges') + .get() + +type cases = [ + Expect> +] + +type Expected = { + foo: number + bar: { + value: string + } + name: string +} diff --git a/utils/index.d.ts b/utils/index.d.ts index 51e3eaeb..1ac6094f 100644 --- a/utils/index.d.ts +++ b/utils/index.d.ts @@ -15,6 +15,8 @@ export type Equal = NotEqual // https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360 export type IsAny = 0 extends (1 & T) ? true : false +export type Debug = { [K in keyof T]: T[K] } + export type ExpectExtends = EXPECTED extends VALUE ? true : false export type ExpectValidArgs any, ARGS extends any[]> = ARGS extends Parameters ? true