Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

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

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-type

Difficulty: :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-strict

Difficulty: :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-latest

Difficulty: :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-keys

Difficulty: :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.


### curry

Difficulty: :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 error

const value: number = bound(100);
```


Check out the answer.


### diff

Difficulty: :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-keys

Difficulty: :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-intersection

Difficulty: :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-automaton

Difficulty: :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.


### randomize

Difficulty: :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