Laissez les composants stylés avoir également une hiérarchie

avant-propos

Récemment, styled-componentslorsque , 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-componentsn'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é

提示.png

code complet

Nous allons mettre en place une styledTiermé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

IHierarchyInputest 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 ( _selfles 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 beforeet 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 beforeet effet de aftermise en œuvre sont cohérents
  • [T: string]Le type correspondant doit beforecontenir afterles 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 来约束范型只能是某些类型)
  • 属性的定义(通过 inkeyof 遍历别的类型来构造一个对象)
  • 类型判断(通过 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"]; // 报错
复制代码

属性的定义

关于 inkeyof 的详细介绍可以看我这篇文章,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];
};
复制代码

Acho que você gosta

Origin juejin.im/post/7078228495255470117
Recomendado
Clasificación