-
Notifications
You must be signed in to change notification settings - Fork 89
Add 35-mapping-modifiers #305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
test: | ||
@ test.sh |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
--- | ||
|
||
name: Модификаторы сопоставления типов (Mapping Modifiers) | ||
theory: | | ||
|
||
При сопоставлении типов можно менять атрибуты свойств такие как неизменность (immutability) и необязательность (optionality). Делается это с помощью соответствующих модификаторов: `readonly` и `?`. | ||
|
||
Чтобы добавить или удалить эти модификаторы, можно использовать префиксы `+` или `-`. Если не использовать префикс, то подразумевается что модификатор будет добавлен, то есть по умолчанию префикс `+`. | ||
|
||
Примеры использования модификаторов есть в Utility Types: | ||
|
||
```typescript | ||
/** | ||
* Делает все свойства типа `T` необязательными, | ||
* то есть добавляет атрибут `?`. | ||
*/ | ||
type Partial<T> = { | ||
[P in keyof T]?: T[P]; | ||
}; | ||
|
||
/** | ||
* Делает все свойства типа `T` обязательными, | ||
* то есть удаляет атрибут `?`. | ||
*/ | ||
type Required<T> = { | ||
[P in keyof T]-?: T[P]; | ||
}; | ||
|
||
/** | ||
* Делает все свойства типа `T` неизменяемыми, | ||
* то есть добавляет атрибут `readonly`. | ||
*/ | ||
type Readonly<T> = { | ||
readonly [P in keyof T]: T[P]; | ||
}; | ||
``` | ||
|
||
Подобным образом можно написать и тип, который делает все свойства типа изменяемыми, то есть удаляет атрибут `readonly`: | ||
|
||
```typescript | ||
type Mutable<T> = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. давай также покажем как используется |
||
-readonly [P in keyof T]: T[P]; | ||
}; | ||
``` | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. нужно добавить небольшой "рекап" - что мы изучили и как оно нам может пригодиться |
||
Благодаря таким типам легче далеть производные типы из уже имеющихся. | ||
|
||
Например, в приложении может быть тип `User` для не авторизованного пользователя у которого все поля не обязательные: | ||
|
||
```typescript | ||
type User = { | ||
id?: string; | ||
firstName?: string; | ||
secondName?: string; | ||
email?: string; | ||
}; | ||
``` | ||
|
||
Из него можно сделать авторизованного пользователя с помощью типа `Required`: | ||
|
||
```typescript | ||
type AuthorizedUser = Required<DefaultUser>; | ||
``` | ||
|
||
instructions: | | ||
|
||
Реализуйте функцию `deepFreeze()`, которая принимает на вход объект и делает его самого, его поля и все вложенные объекты неизменяемыми и возвращает этот объект. | ||
Предполагается что поля объекта и поля вложенных объектов не содержат массивы, только простые типы данных и объекты. | ||
|
||
```typescript | ||
const user = deepFreeze({ | ||
name: 'John', | ||
password: '1q2w3e', | ||
token: 'test', | ||
}); | ||
|
||
user.name = 'Alex'; // Error: Cannot assign to 'name' because it is a read-only property. | ||
``` | ||
|
||
Нужно использовать встроенный в JavaScript метод [Object.freeze()](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze). | ||
|
||
tips: | ||
- | | ||
[Официальная документация Mapped Types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) | ||
- | | ||
[Официальная документация Mapping Modifiers](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#mapping-modifiers) | ||
- | | ||
[Официальная документация Partial](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype) | ||
- | | ||
[Официальная документация Required](https://www.typescriptlang.org/docs/handbook/utility-types.html#requiredtype) | ||
- | | ||
[Официальная документация Readonly](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// BEGIN | ||
type DeepReadonly<T> = { | ||
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. а что делать с массивами? |
||
}; | ||
|
||
const deepFreeze = <T extends object>(obj: T): DeepReadonly<T> => { | ||
const freezedObj = Object.freeze(obj); | ||
|
||
Object.values(freezedObj).forEach((value) => { | ||
if (typeof value === 'object' && value !== null) { | ||
deepFreeze(value); | ||
} | ||
}); | ||
|
||
return freezedObj; | ||
}; | ||
// END | ||
|
||
export default deepFreeze; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
Реализуйте функцию `deepFreeze()`, которая принимает на вход объект и делает его самого, его поля и все вложенные объекты неизменяемыми и возвращает этот объект. | ||
Предполагается что поля объекта и поля вложенных объектов не содержат массивы, только простые типы данных и объекты. | ||
|
||
```typescript | ||
const user = deepFreeze({ | ||
name: 'John', | ||
password: '1q2w3e', | ||
token: 'test', | ||
}); | ||
|
||
user.name = 'Alex'; // Error: Cannot assign to 'name' because it is a read-only property. | ||
``` | ||
|
||
Нужно использовать встроенный в JavaScript метод [Object.freeze()](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
При сопоставлении типов можно менять атрибуты свойств такие как неизменность (immutability) и необязательность (optionality). Делается это с помощью соответствующих модификаторов: `readonly` и `?`. | ||
|
||
Чтобы добавить или удалить эти модификаторы, можно использовать префиксы `+` или `-`. Если не использовать префикс, то подразумевается что модификатор будет добавлен, то есть по умолчанию префикс `+`. | ||
|
||
Примеры использования модификаторов есть в Utility Types: | ||
|
||
```typescript | ||
/** | ||
* Делает все свойства типа `T` необязательными, | ||
* то есть добавляет атрибут `?`. | ||
*/ | ||
type Partial<T> = { | ||
[P in keyof T]?: T[P]; | ||
}; | ||
|
||
/** | ||
* Делает все свойства типа `T` обязательными, | ||
* то есть удаляет атрибут `?`. | ||
*/ | ||
type Required<T> = { | ||
[P in keyof T]-?: T[P]; | ||
}; | ||
|
||
/** | ||
* Делает все свойства типа `T` неизменяемыми, | ||
* то есть добавляет атрибут `readonly`. | ||
*/ | ||
type Readonly<T> = { | ||
readonly [P in keyof T]: T[P]; | ||
}; | ||
``` | ||
|
||
Подобным образом можно написать и тип, который делает все свойства типа изменяемыми, то есть удаляет атрибут `readonly`: | ||
|
||
```typescript | ||
type Mutable<T> = { | ||
-readonly [P in keyof T]: T[P]; | ||
}; | ||
``` | ||
|
||
Благодаря таким типам легче далеть производные типы из уже имеющихся. | ||
|
||
Например, в приложении может быть тип `User` для не авторизованного пользователя у которого все поля не обязательные: | ||
|
||
```typescript | ||
type User = { | ||
id?: string; | ||
firstName?: string; | ||
secondName?: string; | ||
email?: string; | ||
}; | ||
``` | ||
|
||
Из него можно сделать авторизованного пользователя с помощью типа `Required`: | ||
|
||
```typescript | ||
type AuthorizedUser = Required<DefaultUser>; | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
name: Модификаторы сопоставления типов (Mapping Modifiers) | ||
tips: | ||
- > | ||
[Официальная документация Mapped | ||
Types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) | ||
- > | ||
[Официальная документация Mapping | ||
Modifiers](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#mapping-modifiers) | ||
- > | ||
[Официальная документация | ||
Partial](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype) | ||
- > | ||
[Официальная документация | ||
Required](https://www.typescriptlang.org/docs/handbook/utility-types.html#requiredtype) | ||
- > | ||
[Официальная документация | ||
Readonly](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import * as ta from 'type-assertions'; | ||
|
||
import deepFreeze from './index'; | ||
|
||
test('deepFreeze', () => { | ||
const obj = { | ||
name: 'John', | ||
age: 30, | ||
location: { | ||
city: 'York', | ||
coordinates: { | ||
lat: 53.958, | ||
lon: -1.093, | ||
}, | ||
}, | ||
}; | ||
|
||
const user = deepFreeze(obj); | ||
|
||
expect(user).toEqual({ | ||
name: 'John', | ||
age: 30, | ||
location: { | ||
city: 'York', | ||
coordinates: { | ||
lat: 53.958, | ||
lon: -1.093, | ||
}, | ||
}, | ||
}); | ||
|
||
expect(() => { | ||
// @ts-expect-error Cannot assign read-only property. | ||
user.age = 20; | ||
}).toThrow(); | ||
|
||
expect(() => { | ||
// @ts-expect-error Cannot assign nested read-only property. | ||
user.location.city = 'London'; | ||
}).toThrow(); | ||
|
||
ta.assert<ta.Equal<typeof user, Readonly<{ | ||
name: string, | ||
age: number, | ||
location: Readonly<{ | ||
city: string, | ||
coordinates: Readonly<{ | ||
lat: number, | ||
lon: number, | ||
}>, | ||
}>, | ||
}>>>(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Давай подумаем над примером из реальной жизни