prefacio
Recientemente, styled-components
cuando , me encontré con algo que me hizo sentir muy incómodo.
Efecto actual
Aunque varios nodos tienen una relación jerárquica en uso, en styled-components
realidad no hay soporte relevante y solo se puede declarar un nodo por separado.
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>
);
}
复制代码
Esperaba
Espero que los componentes sean jerárquicos, de la siguiente manera:
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>
);
}
复制代码
Hay una estructura jerárquica:
- Disposición
- Diseño.Encabezado
- Diseño.Contenido
- Diseño.Contenido.Izquierda
- Diseño.Contenido.Derecha
- Diseño.Pie de página
Análisis de dificultad
No es difícil cambiar una estructura en mosaico a una estructura de árbol, simplemente escriba una función para manejarla.
La dificultad es cómo hacer que el resultado después de la conversión se dé cuenta de la deducción de tipo ts, es decir, de acuerdo con lo que ingresa, indicando qué subniveles tiene el nivel actual
Sí, este artículo está aquí para enseñarte cómo escribir mecanografiado.
código completo
Vamos a implementar un styledTier
método para hacer esto.
Echemos un vistazo al código completo.
Aplicación.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;
};
复制代码
Explicar con detalle
aporte
IHierarchyInput
se utiliza para restringir la entrada
import { StyledComponent } from "styled-components";
interface IHierarchyInput {
[T: string]: StyledComponent<any, any> | IHierarchyInput;
_self: StyledComponent<any, any>;
}
复制代码
Hay tres puntos clave:
- Definición de propiedades (
_self
las propiedades se definen con tipos especiales) - Tipo de unión (otras propiedades se pueden pasar directamente al componente o a un objeto)
- Tipo de anidamiento de recursión
Definición de propiedades
Construye un objeto 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;
}
复制代码
Pero, ¿qué pasa si quiero hacer una restricción específica en una de las propiedades, como las siguientes propiedades before
y after
:
interface MyTest {
before: string;
[T: string]: number | string | Array<any>;
after: Array<any>;
}
复制代码
Hay dos cosas a tener en cuenta:
- No hay una secuencia de posiciones, por lo que lo anterior
before
yafter
efecto de implementación son consistentes [T: string]
El tipo correspondiente debebefore
contenerafter
los tipos de y
tipo de union
`type StrOrNum = string | number`;
const str: StrOrNum = "abc";
const num: StrOrNum = 123;
复制代码
Los conceptos básicos de mecanografiado, esto no es mucho de qué hablar.
Tipo de anidamiento de recursión
Las definiciones de tipo también pueden usar anidadas recursivamente
type MyArr = Array<number | MyArr>;
const arr: MyArr = [1, 2, 3, [4, 5, 6, [7, 8, 9]]];
复制代码
paradigma funcional
Cómo hacer que la función determine otros parámetros o tipos de valores devueltos según el tipo de parámetros pasados al llamar
这就要用到范型了,基本用法长这样:
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];
};
复制代码