diff --git a/README.zh-CN.md b/README.zh-CN.md index cb954b0a..89e9f4ea 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -30,7 +30,7 @@ TypeScript 类型体操姿势合集 > 点击下方徽章查看题目内容 -
13・Hello World


4・实现 Pick<T, K> 7・实现 Readonly<T> 11・元组转换为对象 14・第一个元素 18・获取元组长度


2・获取函数返回类型 3・实现 Omit<T, K> 8・Readonly 2 9・深度 Readonly 10・元组转合集 12・可串联构造器 15・最后一个元素 16・出堆


6・简单的 Vue 类型 17・科里化 1


5・获取只读字段
By Tags
#4.014・第一个元素 15・最后一个元素 16・出堆 17・科里化 1
#application12・可串联构造器 6・简单的 Vue 类型
#array14・第一个元素 15・最后一个元素 16・出堆 17・科里化 1
#built-in4・实现 Pick<T, K> 7・实现 Readonly<T> 2・获取函数返回类型 3・实现 Omit<T, K>
#deep9・深度 Readonly
#infer2・获取函数返回类型 10・元组转合集
#object-keys7・实现 Readonly<T> 8・Readonly 2 9・深度 Readonly 5・获取只读字段
#readonly7・实现 Readonly<T> 8・Readonly 2 9・深度 Readonly
#this6・简单的 Vue 类型
#tuple18・获取元组长度 10・元组转合集
#union4・实现 Pick<T, K> 3・实现 Omit<T, K> 10・元组转合集
#utils5・获取只读字段
+
13・Hello World


4・实现 Pick<T, K> 7・实现 Readonly<T> 11・元组转换为对象 14・第一个元素 18・获取元组长度


2・获取函数返回类型 3・实现 Omit<T, K> 8・Readonly 2 9・深度 Readonly 10・元组转合集 12・可串联构造器 15・最后一个元素 16・出堆


6・简单的 Vue 类型 17・科里化 1


5・获取只读字段
By Tags
#4.014・第一个元素 15・最后一个元素 16・出堆 17・科里化 1
#application12・可串联构造器 6・简单的 Vue 类型
#array14・第一个元素 15・最后一个元素 16・出堆 17・科里化 1
#built-in4・实现 Pick<T, K> 7・实现 Readonly<T> 2・获取函数返回类型 3・实现 Omit<T, K>
#deep9・深度 Readonly
#infer2・获取函数返回类型 10・元组转合集
#object-keys7・实现 Readonly<T> 8・Readonly 2 9・深度 Readonly 5・获取只读字段
#readonly7・实现 Readonly<T> 8・Readonly 2 9・深度 Readonly
#this6・简单的 Vue 类型
#tuple18・获取元组长度 10・元组转合集
#union4・实现 Pick<T, K> 3・实现 Omit<T, K> 10・元组转合集
#utils5・获取只读字段
## 推荐读物 diff --git a/actions/issue-pr.js b/actions/issue-pr.js index 23d3227c..2d8363b4 100644 --- a/actions/issue-pr.js +++ b/actions/issue-pr.js @@ -74,7 +74,7 @@ module.exports = async(github, context, core) => { && i.title.startsWith(`#${no} `), ) - const dir = `questions/${no}-${slug(info.title)}` + const dir = `questions/${no}-${info.difficulty}-${slug(info.title.replace(/\./g, '-').replace(/<.*>/g, ''))}` const userEmail = `${user.id}+${user.login}@users.noreply.github.com` await PushCommit(github, { @@ -107,7 +107,7 @@ module.exports = async(github, context, core) => { head: `pulls/${no}`, title: `#${no} - ${info.title}`, body: `This is an auto-generated PR that auto reflect on #${no}, please go to #${no} for discussion or making changes.\n\nCloses #${no}`, - labels: ['auto-generated'] + labels: ['auto-generated'], }) core.info('-----Pull Request-----') diff --git a/questions/10-medium-tuple-to-union/README.md b/questions/10-medium-tuple-to-union/README.md index 0e0134b3..5c6a63f4 100644 --- a/questions/10-medium-tuple-to-union/README.md +++ b/questions/10-medium-tuple-to-union/README.md @@ -10,4 +10,4 @@ type Arr = ['1', '2', '3'] const a: TupleToUnion // expected to be '1' | '2' | '3' ``` -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions \ No newline at end of file diff --git a/questions/11-easy-tuple-to-object/README.md b/questions/11-easy-tuple-to-object/README.md index 8495f163..418fdb99 100644 --- a/questions/11-easy-tuple-to-object/README.md +++ b/questions/11-easy-tuple-to-object/README.md @@ -10,4 +10,4 @@ const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const const result: TupleToObject // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'} ``` -
Back Check out Solutions Share your Solutions +
Back Share your Solutions Check out Solutions diff --git a/questions/12-medium-chainable-options/README.md b/questions/12-medium-chainable-options/README.md index 336c3ba1..304d9b46 100644 --- a/questions/12-medium-chainable-options/README.md +++ b/questions/12-medium-chainable-options/README.md @@ -29,4 +29,4 @@ You don't need to write any js/ts logic to handle the problem - just in type lev 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 +
Back Share your Solutions Check out 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 index d2430ca5..d05a98a8 100644 --- a/questions/12-medium-chainable-options/README.zh-CN.md +++ b/questions/12-medium-chainable-options/README.zh-CN.md @@ -29,4 +29,4 @@ interface Result { 你可以假设 `key` 只接受字符串而 `value` 接受任何类型,你只需要暴露它传递的类型而不需要进行任何处理。同样的 `key` 只会被使用一次。 -
返回首页 查看解答 分享你的解答 \ No newline at end of file +
返回首页 分享你的解答 查看解答 \ No newline at end of file diff --git a/questions/13-warm-hello-world/README.md b/questions/13-warm-hello-world/README.md index 2773169b..a9f92109 100644 --- a/questions/13-warm-hello-world/README.md +++ b/questions/13-warm-hello-world/README.md @@ -16,4 +16,4 @@ type test = Expect> Click the `Take the Challenge` button to start coding! Happy Hacking! -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions \ No newline at end of file diff --git a/questions/13-warm-hello-world/README.zh-CN.md b/questions/13-warm-hello-world/README.zh-CN.md index cc12a77f..082f25ad 100644 --- a/questions/13-warm-hello-world/README.zh-CN.md +++ b/questions/13-warm-hello-world/README.zh-CN.md @@ -18,4 +18,4 @@ type test = Expect> 点击上方的 `接受挑战` 开始编码!旅途愉快! -
返回首页 查看解答 分享你的解答 \ No newline at end of file +
返回首页 分享你的解答 查看解答 \ No newline at end of file diff --git a/questions/14-easy-first/README.md b/questions/14-easy-first/README.md index 7fe3cad2..d35ffb8d 100644 --- a/questions/14-easy-first/README.md +++ b/questions/14-easy-first/README.md @@ -13,4 +13,4 @@ type head1 = First // expected to be 'a' type head2 = First // expected to be 3 ``` -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions

Related Challenges

15・Last of Array \ No newline at end of file diff --git a/questions/15-medium-last/README.md b/questions/15-medium-last/README.md index a260e851..859a00ef 100644 --- a/questions/15-medium-last/README.md +++ b/questions/15-medium-last/README.md @@ -14,4 +14,4 @@ type tail1 = Last // expected to be 'c' type tail2 = Last // expected to be 1 ``` -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions

Related Challenges

14・First of Array 16・Pop \ No newline at end of file diff --git a/questions/16-medium-pop/README.md b/questions/16-medium-pop/README.md index df0e074d..4687c77d 100644 --- a/questions/16-medium-pop/README.md +++ b/questions/16-medium-pop/README.md @@ -16,4 +16,4 @@ type re2 = Pop // expected to be [3, 2] **Extra**: Similarly, can you implement `Shift`, `Push` and `Unshift` as well? -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions

Related Challenges

14・First of Array 15・Last of Array \ No newline at end of file diff --git a/questions/17-hard-currying-1/README.md b/questions/17-hard-currying-1/README.md index 15248c5d..c32a2428 100644 --- a/questions/17-hard-currying-1/README.md +++ b/questions/17-hard-currying-1/README.md @@ -21,4 +21,4 @@ In this challenge, the curried function only accept one argument at a time. Once **Extra**: Similarly, can you implement `Shift`, `Push` and `Unshift` as well? -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions

Related Challenges

14・First of Array 16・Pop \ No newline at end of file diff --git a/questions/18-easy-tuple-length/README.md b/questions/18-easy-tuple-length/README.md index 884c2bae..9c632b95 100644 --- a/questions/18-easy-tuple-length/README.md +++ b/questions/18-easy-tuple-length/README.md @@ -12,4 +12,4 @@ type teslaLength = Length // expected 4 type spaceXLength = Length // expected 5 ``` -
Back Check out Solutions Share your Solutions +
Back Share your Solutions Check out Solutions diff --git a/questions/2-medium-return-type/README.md b/questions/2-medium-return-type/README.md index fd8ff2c4..d64d28b2 100644 --- a/questions/2-medium-return-type/README.md +++ b/questions/2-medium-return-type/README.md @@ -15,4 +15,4 @@ const fn = (v: boolean) => { type a = MyReturnType // should be "1 | 2" ``` -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions \ No newline at end of file diff --git a/questions/2-medium-return-type/README.zh-CN.md b/questions/2-medium-return-type/README.zh-CN.md index 744cdde0..736dde3a 100644 --- a/questions/2-medium-return-type/README.zh-CN.md +++ b/questions/2-medium-return-type/README.zh-CN.md @@ -15,4 +15,4 @@ const fn = (v: boolean) => { type a = MyReturnType // 应推导出 "1 | 2" ``` -
返回首页 查看解答 分享你的解答 \ No newline at end of file +
返回首页 分享你的解答 查看解答 \ No newline at end of file diff --git a/questions/3-medium-omit/README.md b/questions/3-medium-omit/README.md index 1c7f364b..b7028702 100644 --- a/questions/3-medium-omit/README.md +++ b/questions/3-medium-omit/README.md @@ -1,4 +1,4 @@ -

Omit<T, K> medium #union #built-in

by Anthony Fu @antfu

Take the Challenge

+

Omit<T, K> medium #union #built-in

by Anthony Fu @antfu

Take the Challenge    简体中文

Implement the built-in `Omit` generic without using it. @@ -13,12 +13,11 @@ interface Todo { completed: boolean } -type TodoPreview = MyOmit +type TodoPreview = MyOmit const todo: TodoPreview = { - title: 'Clean room', completed: false, } ``` -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions

Related Challenges

4・Pick<T, K> \ No newline at end of file diff --git a/questions/3-medium-omit/README.zh-CN.md b/questions/3-medium-omit/README.zh-CN.md new file mode 100644 index 00000000..a3869bc0 --- /dev/null +++ b/questions/3-medium-omit/README.zh-CN.md @@ -0,0 +1,23 @@ +

实现 Omit<T, K> 中等 #union #built-in

by Anthony Fu @antfu

接受挑战    English

+ +不使用 `Omit` 实现 TypeScript 的 `Omit` 范型。 + +`Omit` 会创建一个省略 `K` 中字段的 `T` 对象。 + +例如: + +```ts +interface Todo { + title: string + description: string + completed: boolean +} + +type TodoPreview = MyOmit + +const todo: TodoPreview = { + completed: false, +} +``` + +
返回首页 分享你的解答 查看解答

相关挑战

4・实现 Pick<T, K> \ No newline at end of file diff --git a/questions/3-medium-omit/info.yml b/questions/3-medium-omit/info.yml index 2673bd1d..5b1c3a34 100644 --- a/questions/3-medium-omit/info.yml +++ b/questions/3-medium-omit/info.yml @@ -5,4 +5,6 @@ author: email: hi@antfu.me github: antfu -tags: union, built-in \ No newline at end of file +tags: union, built-in + +related: 4 \ No newline at end of file diff --git a/questions/4-easy-pick/README.md b/questions/4-easy-pick/README.md index e8e7b30b..27f75f15 100644 --- a/questions/4-easy-pick/README.md +++ b/questions/4-easy-pick/README.md @@ -21,4 +21,4 @@ const todo: TodoPreview = { } ``` -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions

Related Challenges

3・Omit<T, K> \ No newline at end of file diff --git a/questions/4-easy-pick/info.yml b/questions/4-easy-pick/info.yml index 95f6c6ed..6c9a4701 100644 --- a/questions/4-easy-pick/info.yml +++ b/questions/4-easy-pick/info.yml @@ -5,4 +5,6 @@ author: email: hi@antfu.me github: antfu -tags: union, built-in \ No newline at end of file +tags: union, built-in + +related: 3 \ No newline at end of file diff --git a/questions/5-extreme-readonly-keys/README.md b/questions/5-extreme-readonly-keys/README.md index f346a0d5..17adcc1b 100644 --- a/questions/5-extreme-readonly-keys/README.md +++ b/questions/5-extreme-readonly-keys/README.md @@ -14,4 +14,4 @@ interface Todo { type Keys = GetReadonlyKeys // expected to be "title" | "description" ``` -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions \ No newline at end of file diff --git a/questions/5-extreme-readonly-keys/info.yml b/questions/5-extreme-readonly-keys/info.yml index 39f97914..d02006a9 100644 --- a/questions/5-extreme-readonly-keys/info.yml +++ b/questions/5-extreme-readonly-keys/info.yml @@ -5,4 +5,4 @@ author: email: hi@antfu.me github: antfu -tags: utils, object-keys \ No newline at end of file +tags: utils, object-keys diff --git a/questions/6-hard-simple-vue/README.md b/questions/6-hard-simple-vue/README.md index 0f52f3bb..1ad28603 100644 --- a/questions/6-hard-simple-vue/README.md +++ b/questions/6-hard-simple-vue/README.md @@ -1,16 +1,16 @@

Simple Vue hard #this #application

by Anthony Fu @antfu

Take the Challenge

-{WIP} Implement a simpiled version of a Vue-like typing support. +Implement a simpiled version of a Vue-like typing support. By providing a function name `SimpleVue` (similar to `Vue.extend` or `defineComponent`), it should properly infer the `this` type inside computed and methods. In this challenge, we assume that SimpleVue take an Object with `data`, `computed` and `methods` fields as it's only argument, -`data` is a simple function that returns a object that expose the the context `this`, +- `data` is a simple function that returns a object that expose the the context `this`, -`computed` is an Object of functions that take the context as `this`, doing some calculation and returns the result. The computed results should be exposed to the context as the plain return values instead of functions. +- `computed` is an Object of functions that take the context as `this`, doing some calculation and returns the result. The computed results should be exposed to the context as the plain return values instead of functions. -`methods` is an Object of functions that take the context as `this` as well. Methods can access the fields exposed by `data`, `computed` as well as other `methods`. The different between `computed` is that `methods` exposed as functions as-is. +- `methods` is an Object of functions that take the context as `this` as well. Methods can access the fields exposed by `data`, `computed` as well as other `methods`. The different between `computed` is that `methods` exposed as functions as-is. The type of `SimpleVue`'s return value can be arbitrary. @@ -36,4 +36,4 @@ const instance = SimpleVue({ }) ``` -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions \ No newline at end of file diff --git a/questions/7-easy-readonly/README.md b/questions/7-easy-readonly/README.md index 243dd818..08a3c0ec 100644 --- a/questions/7-easy-readonly/README.md +++ b/questions/7-easy-readonly/README.md @@ -22,4 +22,4 @@ todo.title = "Hello" // Error: cannot reassign a readonly property todo.description = "barFoo" // Error: cannot reassign a readonly property ``` -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions

Related Challenges

8・Readonly 2 9・Deep Readonly \ No newline at end of file diff --git a/questions/7-easy-readonly/info.yml b/questions/7-easy-readonly/info.yml index 436d3a53..f1fd4555 100644 --- a/questions/7-easy-readonly/info.yml +++ b/questions/7-easy-readonly/info.yml @@ -5,4 +5,6 @@ author: email: hi@antfu.me github: antfu -tags: built-in, readonly, object-keys \ No newline at end of file +tags: built-in, readonly, object-keys + +related: 8, 9 \ No newline at end of file diff --git a/questions/8-medium-readonly-2/README.md b/questions/8-medium-readonly-2/README.md index 948497e5..c548d656 100644 --- a/questions/8-medium-readonly-2/README.md +++ b/questions/8-medium-readonly-2/README.md @@ -24,4 +24,4 @@ todo.description = "barFoo" // Error: cannot reassign a readonly property todo.completed = true // OK ``` -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions

Related Challenges

7・Readonly<T> 9・Deep Readonly \ No newline at end of file diff --git a/questions/8-medium-readonly-2/info.yml b/questions/8-medium-readonly-2/info.yml index 850f78df..28b6eb59 100644 --- a/questions/8-medium-readonly-2/info.yml +++ b/questions/8-medium-readonly-2/info.yml @@ -5,4 +5,6 @@ author: email: hi@antfu.me github: antfu -tags: readonly, object-keys \ No newline at end of file +tags: readonly, object-keys + +related: 7, 9 diff --git a/questions/9-medium-deep-readonly/README.md b/questions/9-medium-deep-readonly/README.md index 91ca6531..516a8bcf 100644 --- a/questions/9-medium-deep-readonly/README.md +++ b/questions/9-medium-deep-readonly/README.md @@ -26,4 +26,4 @@ type Expected = { const todo: DeepReadonly // should be same as `Expaceted` ``` -
Back Check out Solutions Share your Solutions \ No newline at end of file +
Back Share your Solutions Check out Solutions

Related Challenges

7・Readonly<T> 8・Readonly 2 \ No newline at end of file diff --git a/questions/9-medium-deep-readonly/info.yml b/questions/9-medium-deep-readonly/info.yml index 7076a928..d41eaa4c 100644 --- a/questions/9-medium-deep-readonly/info.yml +++ b/questions/9-medium-deep-readonly/info.yml @@ -5,4 +5,6 @@ author: email: hi@antfu.me github: antfu -tags: readonly, object-keys, deep \ No newline at end of file +tags: readonly, object-keys, deep + +related: 7, 8 diff --git a/scripts/loader.ts b/scripts/loader.ts index f9d77721..34b3681e 100644 --- a/scripts/loader.ts +++ b/scripts/loader.ts @@ -36,15 +36,26 @@ export function readmeCleanUp(text: string) { .replace(/[\s\S]*/, '') .trim() } + export function loadInfo(s: string): Partial | undefined { const object = YAML.safeLoad(s) as any if (!object) return undefined - if (object.tags) - object.tags = (object.tags as string).split(',').map(i => i.trim()).filter(Boolean) - else - object.tags = undefined + const arrayKeys = ['tags', 'related'] + + for (const key of arrayKeys) { + if (object[key]) { + object[key] = (object[key] || '') + .toString() + .split(',') + .map((i: string) => i.trim()) + .filter(Boolean) + } + else { + object[key] = undefined + } + } return object } @@ -77,5 +88,6 @@ export async function loadQuizes(): Promise { export function resolveInfo(quiz: Quiz, locale: string = defaultLocale) { const info = Object.assign({}, quiz.info[defaultLocale], quiz.info[locale]) info.tags = quiz.info[locale]?.tags || quiz.info[defaultLocale]?.tags || [] - return info + info.related = quiz.info[locale]?.related || quiz.info[defaultLocale]?.related || [] + return info as QuizMetaInfo } diff --git a/scripts/locales/en.json b/scripts/locales/en.json index 6e978bbb..0fbeb8d9 100644 --- a/scripts/locales/en.json +++ b/scripts/locales/en.json @@ -16,5 +16,6 @@ "link.more-challenges": "More Challenges: ", "link.share-solutions": "Share your solutions: ", "link.view-on-github": "View on Github: ", + "readme.related-challenges": "Related Challenges", "title.question": "Question" } diff --git a/scripts/locales/zh-CN.json b/scripts/locales/zh-CN.json index 71722820..fc3a8999 100644 --- a/scripts/locales/zh-CN.json +++ b/scripts/locales/zh-CN.json @@ -10,10 +10,12 @@ "difficulty.warm": "热身", "display": "简体中文", "divider.code-start": "你的代码", + "divider.further-steps": "下一步", "divider.test-cases": "测试用例", "link.checkout-solutions": "查看解答:", "link.more-challenges": "更多题目:", "link.share-solutions": "分享你的解答:", "link.view-on-github": "在 Github 上查看:", + "readme.related-challenges": "相关挑战", "title.question": "题目" } diff --git a/scripts/readme.ts b/scripts/readme.ts index bb30a511..ce4aae39 100644 --- a/scripts/readme.ts +++ b/scripts/readme.ts @@ -63,6 +63,14 @@ function quizToBadge(quiz: Quiz, locale: string) { ) } +function quizNoToBadges(ids: (string|number)[], quizes: Quiz[], locale: string) { + return ids + .map(i => quizes.find(q => q.no === Number(i))) + .filter(Boolean) + .map(i => quizToBadge(i!, locale)) + .join(' ') +} + function getAllTags(quizes: Quiz[], locale: string) { const set = new Set() for (const quiz of quizes) { @@ -80,7 +88,7 @@ function getQuizesByTag(quizes: Quiz[], locale: string, tag: string) { }) } -async function insertInfoReadme(filepath: string, quiz: Quiz, locale: SupportedLocale) { +async function insertInfoReadme(filepath: string, quiz: Quiz, locale: SupportedLocale, quizes: Quiz[]) { if (!fs.existsSync(filepath)) return let text = await fs.readFile(filepath, 'utf-8') @@ -111,8 +119,9 @@ async function insertInfoReadme(filepath: string, quiz: Quiz, locale: SupportedL /[\s\S]*/, '
' + toBadgeLink(`../../${f('README', locale, 'md')}`, '', t(locale, 'badge.back'), 'grey') + + toBadgeLink(toAnswerShort(quiz.no, locale), '', t(locale, 'badge.share-your-solutions'), 'teal') + toBadgeLink(toSolutionsShort(quiz.no), '', t(locale, 'badge.checkout-solutions'), 'de5a77', '?logo=awesome-lists&logoColor=white') - + toBadgeLink(toAnswerShort(quiz.no, locale), '', t(locale, 'badge.share-your-solutions'), 'green') + + (Array.isArray(info.related) && info.related.length ? `

${t(locale, 'readme.related-challenges')}

${quizNoToBadges(info.related, quizes, locale)}` : '') + '', ) @@ -175,6 +184,7 @@ async function updateQuestionsREADME(quizes: Quiz[]) { ), quiz, locale, + quizes, ) } } diff --git a/scripts/types.ts b/scripts/types.ts index 7ae5fbf4..7a7cb6c4 100644 --- a/scripts/types.ts +++ b/scripts/types.ts @@ -11,6 +11,7 @@ export interface QuizMetaInfo { original_issues: number[] recommended_solutions: number[] tags: string[] + related?: string[] } export type Difficulty = 'warm-up' | 'easy' | 'medium' | 'hard' | 'extreme' | 'pending'