Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/quramy/type-dungeon
TypeScript code exercise
https://github.com/quramy/type-dungeon
training typescript
Last synced: 2 months ago
JSON representation
TypeScript code exercise
- Host: GitHub
- URL: https://github.com/quramy/type-dungeon
- Owner: Quramy
- License: mit
- Created: 2020-03-11T11:23:32.000Z (almost 5 years ago)
- Default Branch: master
- Last Pushed: 2024-10-23T14:15:18.000Z (2 months ago)
- Last Synced: 2024-10-24T21:16:36.120Z (2 months ago)
- Topics: training, typescript
- Language: TypeScript
- Homepage:
- Size: 330 KB
- Stars: 31
- Watchers: 4
- Forks: 2
- Open Issues: 11
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# Type Dungeon
TypeScript code exercise.
- [Exercise](#exercise)
- [array-to-union](#array-to-union)
- [json-type](#json-type)
- [to-strict](#to-strict)
- [combine-latest](#combine-latest)
- [convert-object-keys](#convert-object-keys)
- [curry](#curry)
- [diff](#diff)
- [only-required-keys](#only-required-keys)
- [union-to-intersection](#union-to-intersection)
- [cellular-automaton](#cellular-automaton)
- [randomize](#randomize)
- [How to create new exercise](#how-to-create-new-exercise)
- [LICENSE](#license)## Exercise
### array-to-union
Difficulty: :space_invader:
Play this with TypeScript playground !
```typescript
// Complete the following `toUnion` type to match the following excepcted condition.
type toUnion> = unknown;// Expected
const keys = ["age", "firstName", "lastName"] as const;
type Keys = toUnion; // should be "age" | "firstName" | "lastName"
```
Check out the answer.
### json-typeDifficulty: :space_invader:
Play this with TypeScript playground !
```typescript
// Complete the `JSONLike` type. It represents type of values `JSON.parse()` returns.
type JSONLike = unknown;const v1: JSONLike = {};
const v2: JSONLike = [];
const v3: JSONLike = null;
const v4: JSONLike = {
flag: false,
name: "name",
num: 100,
arr: [{ hoge: "hoge" }],
optionParam: null,
};// The following RHSs can not be serialized to JSON
const v5: JSONLike = () => 1; // should be error
const v6: JSONLike = { fn: () => 1 }; // should be error
```
Check out the answer.
### to-strictDifficulty: :space_invader:
Play this with TypeScript playground !
```typescript
// Edit the right hand side to match the following expected type conditions.
type Strict = unknown;// Expected
type SomeObjectType = {
name?: string;
age?: number;
};declare const strictObj: Strict;
const userName: string = strictObj.name; // typeof name should not be undefined / null
const age: number = strictObj.age; // typeof age should not be undefined / null
```
Check out the answer.
### combine-latestDifficulty: :space_invader::space_invader:
Play this with TypeScript playground !
```typescript
// Complete the following `combineLatest` function declaration.
//
// This function is used with RxJS API. See https://rxjs.dev/api/index/function/combineLatest .
interface Observable {
subscribe(cb: (v: T) => void): void;
}declare function combineLatest(...args: Observable[]): Observable;
// Expected
declare const s1: Observable;
declare const s2: Observable;
declare const s3: Observable;combineLatest(s1, s2, s3).subscribe(([v1, v2, v3]) => {
// v1 should be string
const result1: string = v1;// v2 should be number
const result2: number = v2;// v3 should be boolean
const result3: boolean = v3;
});
```
Check out the answer.
### convert-object-keysDifficulty: :space_invader::space_invader:
Play this with TypeScript playground !
```typescript
// Complete the `CamelizedObject` type operator to match the following expected condition.
//
// Imagine object from response of Web API implemented by language which prefer "snake-cased" variable name (e.g. Ruby).
// But you want to convert the response objects to objects whose keys are "camel-cased" strings.
//
// So you need to this type operator to represents the type of the converted objects.
type CamelizedObject = unknown;// Expected
type GeneratedResponseType = {
id: string;
first_name: string;
last_name: string;
created_at: number;
};const responseObj: GeneratedResponseType = {
id: "10000",
first_name: "Yosuke",
last_name: "Kurami",
created_at: 1606389092229,
};const converted: {
id: string;
firstName: string;
lastName: string;
createdAt: number;
} = convertKeysFromSnake2Camel(responseObj);// The following function converts keys in input object to camel-cased string.
function convertKeysFromSnake2Camel>(obj: T): CamelizedObject {
const keys = Object.keys(obj) as string[];
return keys.reduce((acc, k) => {
const camelizedKey = snake2Camel(k);
return {
...acc,
[camelizedKey]: obj[k],
};
}, {} as any);
}// hoGe -> Hoge
function capitalize(input: string) {
return input.slice(0, 1).toUpperCase() + input.slice(1).toLowerCase();
}// hoge_foo -> HogeFoo
function snake2Pascal(input: string) {
return input.split("_").map(capitalize).join("");
}// hoge_foo -> hogeFoo
function snake2Camel(input: string) {
const p = snake2Pascal(input);
return p.slice(0, 1).toLowerCase() + p.slice(1);
}
```
Check out the answer.
### curryDifficulty: :space_invader::space_invader:
Play this with TypeScript playground !
```typescript
// Complete the function type to match the following expected type condition.
declare function curry(fn: Function): any;// Expected
const add = (a: number, b: number) => a + b;
const bound = curry(add)(1);// @ts-expect-error `bound` should accept 1 argument
bound();// @ts-expect-error `bound` should accept 1 argument
bound(100, 100); // should throw errorconst value: number = bound(100);
```
Check out the answer.
### diffDifficulty: :space_invader::space_invader:
Play this with TypeScript playground !
```typescript
// Complete `$Diff` type to match the following condition.
// FYI, Flow type has the same name utility type.
type $Diff = any;type Props = {
id: number;
title: string;
tags: string[];
};const defaultProps = {
tags: [],
};type RequiredProps = $Diff;
const props1: RequiredProps = {
id: 100,
title: "my post",
};const props2: RequiredProps = {
id: 100,
title: "my post",
tags: ["ts"],
};// @ts-expect-error
const props3: RequiredProps = {
id: 100,
tags: [],
};// @ts-expect-error
const props4: RequiredProps = {
title: "my post",
};
```
Check out the answer.
### only-required-keysDifficulty: :space_invader::space_invader:
Play this with TypeScript playground !
```typescript
// Replace the RHS of the following `RequiredKeys` type to match the expected section.
type RequiredKeys = keyof T;// Expected
type User = {
id: number;
lastName: string;
firstName: string;
middleName?: string;
};const validKeys: RequiredKeys[] = ["id", "lastName", "firstName"];
// @ts-expect-error 'middleName' is an optional key in User type
const invalidKeys: RequiredKeys[] = ["middleName"];
```
Check out the answer.
### union-to-intersectionDifficulty: :space_invader::space_invader:
Play this with TypeScript playground !
```typescript
// Complete the following `U2I` type to match the following excepcted condition.
// `U2I` converts from union type to intersection type.
type U2I = unknown;type User = {
id: string;
name: string;
age: number;
};type Post = {
id: string;
name: string;
body: string;
};type UserOrPost = User | Post;
// @ts-expect-error
const x1: U2I = {
id: "HOGE",
name: "hoge",
age: 20,
};// @ts-expect-error
const x2: U2I = {
id: "FOO",
name: "foo",
body: "foo body",
};const x3: U2I = {
id: "BAR",
name: "bar",
age: 20,
body: "bar body",
};
```
Check out the answer.
### cellular-automatonDifficulty: :space_invader::space_invader::space_invader:
Play this with TypeScript playground !
```typescript
// Complete the following type `CellularAutomaton` using this `Rule` type.
// The `Rule` type represents a rule of one-dimensional cellular automan, a.k.a. Wolfram code "110".
// Read https://en.wikipedia.org/wiki/Rule_110 if you want more detail.
//
// `CellularAutomaton` should give the N-th generation state when starting `[1]`.
// For example, `CellularAutomaton<1>` provides `[1,1,0]` because `[0,0,1]` generates `1`, [0,1,0] does `1`, and `[1,0,0]` does `0`.
type B = 1 | 0;
// prettier-ignore
type Rule110 =
T extends [1, 1, 1] ? 0 :
T extends [1, 1, 0] ? 1 :
T extends [1, 0, 1] ? 1 :
T extends [1, 0, 0] ? 0 :
T extends [0, 1, 1] ? 1 :
T extends [0, 1, 0] ? 1 :
T extends [0, 0, 1] ? 1 :
T extends [0, 0, 0] ? 0 :
never;type Rule = Rule110;
type CellularAutomaton = unknown;
// Expected
const s0: CellularAutomaton<0> = [1] // prettier-ignore
const s1: CellularAutomaton<1> = [1,1,0] // prettier-ignore
const s2: CellularAutomaton<2> = [1,1,1,0,0] // prettier-ignore
const s3: CellularAutomaton<3> = [1,1,0,1,0,0,0] // prettier-ignore
const s4: CellularAutomaton<4> = [1,1,1,1,1,0,0,0,0] // prettier-ignore
const s5: CellularAutomaton<5> = [1,1,0,0,0,1,0,0,0,0,0] // prettier-ignore
const s6: CellularAutomaton<6> = [1,1,1,0,0,1,1,0,0,0,0,0,0] // prettier-ignore
```
Check out the answer.
### randomizeDifficulty: :space_invader::space_invader::space_invader:
Play this with TypeScript playground !
```typescript
// Complete the `Random` type.
type Random = unknown;declare const random: Random;
const a = { value: 1 } as const;
const b = { value: 2 } as const;
const c = { value: 3 } as const;// Expected:
// The `random` return type should be assignable this type annotation.const valueAB = random(a, b);
const valueABC = random(a, b, c);const x: { readonly value: 1 } | { readonly value: 2 } = valueAB;
const y: { readonly value: 1 } | { readonly value: 2 } | { readonly value: 3 } = valueABC;
```
Check out the answer.
## How to create new exercise
Read [CONTRIBUTING.md](./CONTRIBUTING.md).## LICENSE
MIT