This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type LoginFormProps = { | |
title?: React.ReactNode; // ⬅ Хоп 1️⃣ | |
email?: string; | |
password?: string; | |
} | |
function LoginForm(props: LoginFormProps) { | |
return ( | |
<Form> | |
<div className="header"> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type LoginFormProps = { | |
email?: string; | |
password?: string; | |
} | |
function LoginForm(props: LoginFormProps) { | |
return ( | |
<Form> | |
<div className="header"> | |
<h2>Вход</h2> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1️⃣Первым делом нам понадобиться тип извлекающий `DescriptorWithMetaMap` | |
type GetMetaDeps<T> = T extends {deps?: infer D} ? GetMeta<D> : never; | |
// Реузльтат для GetMetaDeps<FormProps>, как я уже писал выше, будет: | |
// { | |
// Input: DescriptorWithMeta<"myapp/Input", InputProps>; | |
// Btn: DescriptorWithMeta<"myapp/Btn", BtnProps>; | |
// } | |
// 2️⃣Теперь нам надо преобразовать этот объект в `depId => LikeComponent<{...}>` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1️⃣ Иконка (без зависимостей) | |
type IconProps = { | |
name: string; | |
theme?: Theme<{...}>; | |
} | |
// Декскриптор иконки | |
const $Icon = createComponentDescriptor('myapp/Icon', {} as IconProps, {}); | |
// Компонент на основе дескриптора иконки |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Вот теперь можно описать нужный нам тип | |
type DepsInline<T extends DescriptorWithMetaMap> = CastIntersect< // 3️⃣ | |
{ // ⬇️Итерируемся по ключам и проверяем, является ли свойство | |
// опциональным или нет, если да, то создаём объект с необязальным ключем | |
[K in keyof T]: IsOptional<T[K]> extends true // 1️⃣ | |
? { [X in K]?: LikeComponent<NonNullable<T[K]>['meta']> } | |
: { [X in K]: LikeComponent<NonNullable<T[K]>['meta']> } | |
}[keyof T], // 2️⃣ | |
object | |
> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Вспомогательный тип, который добавляет `undefined` | |
type Optional<T> = T | undefined; | |
// Тип, который проверяет на `optional`, путем преобразования входящего | |
// дженерика в `intersect` и проверкой на вхождение в него `undefined` | |
type IsOptional<T> = ToIntersect<T> extends undefined ? true : false; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export type UnionToIntersect<U> = | |
(U extends any | |
? (inp: U) => void | |
: never | |
) extends ((out: infer I) => void) | |
? I | |
: never | |
; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Думаю тут нет нужды в объяснениях: | |
export type Cast<X, Y> = X extends Y ? X : Y; | |
// А вот для `Meta`, нам понадобиться символ, ибо его «можно» ⤵️ (см. ниже) | |
export const __meta__ = Symbol('__meta__'); | |
// «Можно» использовать для описания свойства объекта: | |
export type Meta<T> = { | |
[__meta__]?: T; // так же можно использовать и литералы, но символ пизже! | |
// т.е. его нельзя будет увидеть через autocomplete 👀 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Создаём дескриптор компонента с описанием его зависимостей | |
const $Form = createComponentDescriptor('@truekit/Form', {} as FormProps, { | |
// Как я уже говорил, описание зависимосей являются другие дескрипторы | |
Input: $Input, | |
Button: $Button, | |
Checkbox: $Checkbox.optional(), // необязательная зависимость | |
}); | |
type FormProps = { | |
// бли-бла-бла |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export interface Descriptor<ID extends string> { | |
readonly id: ID; | |
readonly isOptional: boolean; | |
optional(): this | undefined; // ⬅️Вот это очень важный момент, | |
// undefined нужен как часть типа❗️ | |
// Дальше, этот undefined нужен будет, | |
// чтобы определить, является ли зависисимость | |
// обязательной, или нет | |
} |