avant-propos
Récemment, styled-components
lorsque , j'ai rencontré quelque chose qui m'a mis très mal à l'aise
Effet actuel
Bien que plusieurs nœuds aient une relation hiérarchique en cours d'utilisation, il styled-components
n'y a en fait aucune prise en charge pertinente et un seul nœud peut être déclaré séparément.
import styled from "styled-components";
const Layout = styled.div``;
const LayoutHeader = styled.div``;
const LayoutContent = styled.div``;
const LayoutContentLeft = styled.div``;
const LayoutContentRight = styled.div``;
const LayoutFooter = styled.div``;
function App() {
return (
<Layout>
<LayoutHeader>头部</LayoutHeader>
<LayoutContent>
主体
<LayoutContentLeft>主体的左边</LayoutContentLeft>
<LayoutContentRight>主体的右边</LayoutContentRight>
</LayoutContent>
<LayoutFooter>尾部</LayoutFooter>
</Layout>
);
}
复制代码
j'esperais
Je m'attends à ce que les composants soient hiérarchiques, comme suit :
function App() {
return (
<Layout>
<Layout.Header>头部</Layout.Header>
<Layout.Content>
主体
<Layout.Content.Left>主体的左边</Layout.Content.Left>
<Layout.Content.Right>主体的右边</Layout.Content.Right>
</Layout.Content>
<Layout.Footer>尾部</Layout.Footer>
</Layout>
);
}
复制代码
Il existe une structure hiérarchique :
- Mise en page
- Layout.Header
- Disposition.Contenu
- Disposition.Contenu.Gauche
- Disposition.Contenu.Droite
- Mise en page.Pied de page
Analyse de la difficulté
Il n'est pas difficile de changer une structure tuilée en une structure arborescente, il suffit d'écrire une fonction pour s'en occuper
La difficulté est de savoir comment faire en sorte que le résultat après la conversion réalise sa déduction de type ts, c'est-à-dire, en fonction de ce que vous avez entré, en demandant quels sous-niveaux le niveau actuel a
Oui, cet article est là pour vous apprendre à écrire dactylographié
code complet
Nous allons mettre en place une styledTier
méthode pour ce faire
Regardons le code complet
App.tsx
import { styledTier } from "./utils.ts";
/**
* 每一级可以单传一个组件也可以传一个对象
* 如果存在下一层则只能传对象
**/
const Layout = styledTier({
_self: styled.div``,
Header: styled.div``, // 单传一个组件
Content: {
// 传一个对象
_self: styled.div``, // "_self" 属性表示本层组件
Left: styled.div``,
Right: styled.div``,
},
Footer: styled.div``,
});
export default function App() {
return (
<Layout>
<Layout.Header>头部</Layout.Header>
<Layout.Content>
主体
<Layout.Content.Left>主体的左边</Layout.Content.Left>
<Layout.Content.Right>主体的右边</Layout.Content.Right>
</Layout.Content>
<Layout.Footer>尾部</Layout.Footer>
</Layout>
);
}
复制代码
utils.ts
import { StyledComponent } from "styled-components";
interface IHierarchyInput {
[T: string]: StyledComponent<any, any> | IHierarchyInput;
_self: StyledComponent<any, any>;
}
type IHierarchyOutput<Input extends IHierarchyInput = any> = Input["_self"] & {
[Prop in keyof Omit<Input, "_self">]: Input[Prop] extends IHierarchyInput
? IHierarchyOutput<Input[Prop]>
: Input[Prop];
};
export const styledTier = function <O extends IHierarchyInput>(
input: O
): IHierarchyOutput<O> {
const { _self: component, ...children } = input;
const current: any = component;
for (const [pKey, pItem] of Object.entries(children)) {
if (pItem._self) {
current[pKey] = styledTier(pItem);
} else {
current[pKey] = pItem;
}
}
return current;
};
复制代码
Expliquer en détail
contribution
IHierarchyInput
est utilisé pour contraindre l'entrée
import { StyledComponent } from "styled-components";
interface IHierarchyInput {
[T: string]: StyledComponent<any, any> | IHierarchyInput;
_self: StyledComponent<any, any>;
}
复制代码
Il y a trois points clés :
- Définition des propriétés (
_self
les propriétés sont définies avec des types spéciaux) - Type d'union (d'autres propriétés peuvent être transmises directement au composant ou à un objet)
- Type d'imbrication de récursivité
Définition des propriétés
Construire un objet simple :
// 属性名为 string,属性值为 number
interface MyObj {
[T: string]: number;
}
// 属性名为 number,属性值为 number(其实这样没有意义,因为对象在 js 中属性名都会转成 string)
interface MyObj {
[T: number]: number;
}
// 或者还可以这样(这样只在 ts 中有意义,而在 js 中是没有意义的)
interface MyObj {
[T: string]: number;
[T: number]: number;
}
复制代码
Mais que se passe-t-il si je veux faire une contrainte spécifique sur l'une des propriétés, telles que les propriétés suivantes before
et after
:
interface MyTest {
before: string;
[T: string]: number | string | Array<any>;
after: Array<any>;
}
复制代码
Il y a deux choses à noter :
- Il n'y a pas de séquence de positions, donc ce qui précède
before
et effet deafter
mise en œuvre sont cohérents [T: string]
Le type correspondant doitbefore
contenirafter
les types de et
type de syndicat
`type StrOrNum = string | number`;
const str: StrOrNum = "abc";
const num: StrOrNum = 123;
复制代码
Les bases du tapuscrit, ce n'est pas grand chose à dire
Type d'imbrication de récursivité
Les définitions de type peuvent également utiliser des éléments imbriqués récursivement
type MyArr = Array<number | MyArr>;
const arr: MyArr = [1, 2, 3, [4, 5, 6, [7, 8, 9]]];
复制代码
paradigme fonctionnel
Comment faire en sorte que la fonction détermine d'autres paramètres ou renvoie des types de valeur en fonction du type de paramètres transmis lors de l' appel
这就要用到范型了,基本用法长这样:
function fn<T>(){}
举个简单的例子:
// 定义范型 T,分别用于两个参数的不同位置,用以确定这两个位置最终类型会是一致
function definition<T>(input: T, callback: (result: T) => void) {
callback(input);
}
definition(2, function (result) {
console.log(result); // 提示为 number
});
definition("i'm sb", function (result) {
console.log(result); // 提示为 string
});
复制代码
返回值
IHierarchyOutput
是用来约束返回值的
type IHierarchyOutput<Input extends IHierarchyInput = any> = Input["_self"] & {
[Prop in keyof Omit<Input, "_self">]: Input[Prop] extends IHierarchyInput
? IHierarchyOutput<Input[Prop]>
: Input[Prop];
};
复制代码
首先,有以下几个知识点:
- 范型
- 约束范型的类型(通过
extends
来约束范型只能是某些类型) - 属性的定义(通过
in
和keyof
遍历别的类型来构造一个对象) - 类型判断(通过
extends
还有三目运算符来实现类型的判断和转化) - 递归嵌套结构
范型
这里不过多陈述了,都是基础,举个简单的例子吧:
// 定义一个有范型的类型
type MyTuple<A, B> = [A, B];
const t1: MyTuple<string, number> = ["abc", 123];
const t2: MyTuple<number, number> = [456, 123];
复制代码
约束范型的类型
extends
关键字字面意思是“继承”,同时他也可以用来约束范型必须是什么类型,譬如
// A 必须是 string 或者是 number,而 B 必须是 number
type MyTuple<A extends string | number, B extends number> = [A, B];
const t1: MyTuple<number, number> = [456, 123];
const t2: MyTuple<boolean, number> = [false, 123]; // 报错
const t2: MyTuple<number, string> = [456, "abc"]; // 报错
复制代码
属性的定义
关于 in
和 keyof
的详细介绍可以看我这篇文章,Typescript 关键字
类型判断
关于 extends
的详细介绍可以看我这篇文章,Typescript 关键字
递归嵌套结构
type IHierarchyOutput<Input extends IHierarchyInput = any> = Input["_self"] & {
// 排除 _self
[Prop in keyof Omit<Input, "_self">]: Input[Prop] extends IHierarchyInput // 判断当前属性值是否还是对象结构
? IHierarchyOutput<Input[Prop]> // 如果是,则把当前属性值作为范型重新传入 IHierarchyOutput 中,开启下一轮循环
: Input[Prop];
};
复制代码