This commit is contained in:
Anthony Fu 2020-07-27 22:41:46 +08:00
parent 2d2a0a644f
commit 11e4385368
9 changed files with 102 additions and 2 deletions

File diff suppressed because one or more lines are too long

View File

@ -28,7 +28,7 @@ TypeScript 类型体操姿势合集
> 点击下方徽章查看题目内容
<!--challenges-start-->
<img src="https://img.shields.io/badge/%E7%AE%80%E5%8D%95-%20-90bb12" alt=" "/><br><a href="./questions/4-easy-pick/README.md" target="_blank"><img src="https://img.shields.io/badge/-%234%E3%83%BB%E5%AE%9E%E7%8E%B0%20Pick%3CT%2C%20K%3E-90bb12" alt="#4・实现 Pick<T, K>"/></a> <a href="./questions/7-easy-readonly/README.md" target="_blank"><img src="https://img.shields.io/badge/-%237%E3%83%BB%E5%AE%9E%E7%8E%B0%20Readonly%3CT%3E-90bb12" alt="#7・实现 Readonly<T>"/></a> <br><br><img src="https://img.shields.io/badge/%E4%B8%AD%E7%AD%89-%20-eaa648" alt=" "/><br><a href="./questions/2-medium-return-type/README.zh-CN.md" target="_blank"><img src="https://img.shields.io/badge/-%232%E3%83%BB%E8%8E%B7%E5%8F%96%E5%87%BD%E6%95%B0%E8%BF%94%E5%9B%9E%E7%B1%BB%E5%9E%8B-eaa648" alt="#2・获取函数返回类型"/></a> <a href="./questions/3-medium-omit/README.md" target="_blank"><img src="https://img.shields.io/badge/-%233%E3%83%BB%E5%AE%9E%E7%8E%B0%20Omit%3CT%2C%20K%3E-eaa648" alt="#3・实现 Omit<T, K>"/></a> <a href="./questions/8-medium-readonly-2/README.md" target="_blank"><img src="https://img.shields.io/badge/-%238%E3%83%BBReadonly%202-eaa648" alt="#8・Readonly 2"/></a> <a href="./questions/9-medium-deep-readonly/README.md" target="_blank"><img src="https://img.shields.io/badge/-%239%E3%83%BB%E6%B7%B1%E5%BA%A6%20Readonly-eaa648" alt="#9・深度 Readonly"/></a> <a href="./questions/10-medium-tuple-to-union/README.md" target="_blank"><img src="https://img.shields.io/badge/-%2310%E3%83%BB%E5%85%83%E7%BB%84%E8%BD%AC%E5%90%88%E9%9B%86-eaa648" alt="#10・元组转合集"/></a> <br><br><img src="https://img.shields.io/badge/%E5%9B%B0%E9%9A%BE-%20-red" alt=" "/><br><a href="./questions/5-hard-readonly-keys/README.md" target="_blank"><img src="https://img.shields.io/badge/-%235%E3%83%BB%E8%8E%B7%E5%8F%96%E5%8F%AA%E8%AF%BB%E5%AD%97%E6%AE%B5-red" alt="#5・获取只读字段"/></a> <br><br><img src="https://img.shields.io/badge/%E5%9C%B0%E7%8B%B1-%20-b11b8d" alt=" "/><br><a href="./questions/6-extreme-simple-vue/README.md" target="_blank"><img src="https://img.shields.io/badge/-%236%E3%83%BB%E7%AE%80%E5%8D%95%E7%9A%84%20Vue%20%E7%B1%BB%E5%9E%8B-b11b8d" alt="#6・简单的 Vue 类型"/></a> <br><details><summary>By Tags</summary><br><table><tbody></tbody></table></details>
<img src="https://img.shields.io/badge/%E7%AE%80%E5%8D%95-%20-90bb12" alt=" "/><br><a href="./questions/4-easy-pick/README.md" target="_blank"><img src="https://img.shields.io/badge/-%234%E3%83%BB%E5%AE%9E%E7%8E%B0%20Pick%3CT%2C%20K%3E-90bb12" alt="#4・实现 Pick<T, K>"/></a> <a href="./questions/7-easy-readonly/README.md" target="_blank"><img src="https://img.shields.io/badge/-%237%E3%83%BB%E5%AE%9E%E7%8E%B0%20Readonly%3CT%3E-90bb12" alt="#7・实现 Readonly<T>"/></a> <br><br><img src="https://img.shields.io/badge/%E4%B8%AD%E7%AD%89-%20-eaa648" alt=" "/><br><a href="./questions/2-medium-return-type/README.zh-CN.md" target="_blank"><img src="https://img.shields.io/badge/-%232%E3%83%BB%E8%8E%B7%E5%8F%96%E5%87%BD%E6%95%B0%E8%BF%94%E5%9B%9E%E7%B1%BB%E5%9E%8B-eaa648" alt="#2・获取函数返回类型"/></a> <a href="./questions/3-medium-omit/README.md" target="_blank"><img src="https://img.shields.io/badge/-%233%E3%83%BB%E5%AE%9E%E7%8E%B0%20Omit%3CT%2C%20K%3E-eaa648" alt="#3・实现 Omit<T, K>"/></a> <a href="./questions/8-medium-readonly-2/README.md" target="_blank"><img src="https://img.shields.io/badge/-%238%E3%83%BBReadonly%202-eaa648" alt="#8・Readonly 2"/></a> <a href="./questions/9-medium-deep-readonly/README.md" target="_blank"><img src="https://img.shields.io/badge/-%239%E3%83%BB%E6%B7%B1%E5%BA%A6%20Readonly-eaa648" alt="#9・深度 Readonly"/></a> <a href="./questions/10-medium-tuple-to-union/README.md" target="_blank"><img src="https://img.shields.io/badge/-%2310%E3%83%BB%E5%85%83%E7%BB%84%E8%BD%AC%E5%90%88%E9%9B%86-eaa648" alt="#10・元组转合集"/></a> <a href="./questions/12-medium-chainable-options/README.zh-CN.md" target="_blank"><img src="https://img.shields.io/badge/-%2312%E3%83%BB%E5%8F%AF%E4%B8%B2%E8%81%94%E6%9E%84%E9%80%A0%E5%99%A8-eaa648" alt="#12・可串联构造器"/></a> <br><br><img src="https://img.shields.io/badge/%E5%9B%B0%E9%9A%BE-%20-red" alt=" "/><br><a href="./questions/5-hard-readonly-keys/README.md" target="_blank"><img src="https://img.shields.io/badge/-%235%E3%83%BB%E8%8E%B7%E5%8F%96%E5%8F%AA%E8%AF%BB%E5%AD%97%E6%AE%B5-red" alt="#5・获取只读字段"/></a> <br><br><img src="https://img.shields.io/badge/%E5%9C%B0%E7%8B%B1-%20-b11b8d" alt=" "/><br><a href="./questions/6-extreme-simple-vue/README.md" target="_blank"><img src="https://img.shields.io/badge/-%236%E3%83%BB%E7%AE%80%E5%8D%95%E7%9A%84%20Vue%20%E7%B1%BB%E5%9E%8B-b11b8d" alt="#6・简单的 Vue 类型"/></a> <br><details><summary>By Tags</summary><br><table><tbody></tbody></table></details>
<!--challenges-end-->
## 推荐读物

View File

@ -0,0 +1,32 @@
<!--info-header-start--><h1>Chainable Options <img src="https://img.shields.io/badge/-medium-eaa648" alt="medium"/> <img src="https://img.shields.io/badge/-%23application-999" alt="#application"/></h1><blockquote><p>by Anthony Fu <a href="https://github.com/antfu" target="_blank">@antfu</a></p></blockquote><a href="https://type-challenges.netlify.app/case/12/play" target="_blank"><img src="https://img.shields.io/badge/-Take%20the%20Challenge-3178c6?logo=typescript" alt="Take the Challenge"/></a> <br><br><!--info-header-end-->
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.
<!--info-footer-start--><a href="../../README.md" target="_blank"><img src="https://img.shields.io/badge/-Back-grey" alt="Back"/></a> <a href="https://type-challenges.netlify.app/case/12/solutions" target="_blank"><img src="https://img.shields.io/badge/-Check%20out%20Solutions-de5a77?logo=awesome-lists&logoColor=white" alt="Check out Solutions"/></a> <a href="https://type-challenges.netlify.app/case/12/answer" target="_blank"><img src="https://img.shields.io/badge/-Share%20your%20Solutions-green" alt="Share your Solutions"/></a> <!--info-footer-end-->

View File

@ -0,0 +1,32 @@
<!--info-header-start--><h1>可串联构造器 <img src="https://img.shields.io/badge/-%E4%B8%AD%E7%AD%89-eaa648" alt="中等"/> </h1><blockquote><p>by Anthony Fu <a href="https://github.com/antfu" target="_blank">@antfu</a></p></blockquote><a href="https://type-challenges.netlify.app/case/12/play/zh-CN" target="_blank"><img src="https://img.shields.io/badge/-%E6%8E%A5%E5%8F%97%E6%8C%91%E6%88%98-3178c6?logo=typescript" alt="接受挑战"/></a> <br><br><!--info-header-end-->
在 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` 只会被使用一次。
<!--info-footer-start--><a href="../../README.zh-CN.md" target="_blank"><img src="https://img.shields.io/badge/-%E8%BF%94%E5%9B%9E%E9%A6%96%E9%A1%B5-grey" alt="返回首页"/></a> <a href="https://type-challenges.netlify.app/case/12/solutions" target="_blank"><img src="https://img.shields.io/badge/-%E6%9F%A5%E7%9C%8B%E8%A7%A3%E7%AD%94-de5a77?logo=awesome-lists&logoColor=white" alt="查看解答"/></a> <a href="https://type-challenges.netlify.app/case/12/answer/zh-CN" target="_blank"><img src="https://img.shields.io/badge/-%E5%88%86%E4%BA%AB%E4%BD%A0%E7%9A%84%E8%A7%A3%E7%AD%94-green" alt="分享你的解答"/></a> <!--info-footer-end-->

View File

@ -0,0 +1,8 @@
title: Chainable Options
author:
name: Anthony Fu
email: hi@antfu.me
github: antfu
tags: application

View File

@ -0,0 +1 @@
title: 可串联构造器

View File

@ -0,0 +1,4 @@
type Chainable = {
option(key: string, value: any): any
get(): any
}

View File

@ -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<Equal<typeof result, Expected>>
]
type Expected = {
foo: number
bar: {
value: string
}
name: string
}

2
utils/index.d.ts vendored
View File

@ -15,6 +15,8 @@ export type Equal<VALUE extends EXPECTED, EXPECTED> = NotEqual<VALUE, EXPECTED>
// https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360
export type IsAny<T> = 0 extends (1 & T) ? true : false
export type Debug<T> = { [K in keyof T]: T[K] }
export type ExpectExtends<VALUE, EXPECTED> = EXPECTED extends VALUE ? true : false
export type ExpectValidArgs<FUNC extends (...args: any[]) => any, ARGS extends any[]> = ARGS extends Parameters<FUNC>
? true