As is known to all

A major upgrade from [email protected] is Icon tweaking, which purifies the original
component into another package for the following reasons

In [email protected], we introduced SVG ICONS (why use SVG ICONS?). . The icon API with string naming cannot be loaded on demand, so full SVG icon files are introduced, which greatly increases the size of the package. In 4.0, we adjusted the usage API for ICONS to support Tree Shaking and reduced the antD default package size by about 150 KB(Gzipped). The original 🎉 🎉 🎉

The reduction in packaging size is immediate.

There is No Silver Bullet

Such a handsome optimization of course also need to pay a price, a loss of less pleasant use experience, such as this

  // tree-shaking supported
- import { Icon } from 'antd';
+ import { SmileOutlined } from '@ant-design/icons';

  const Demo = () => (
    <div>
- 
      
+ 
      
      <Button icon={<SmileOutlined />} />
    </div>
  );
Copy the code

Before: Just import an Icon component and declare which component type indicates

After: Importing a specific component such as IconName is required. In typescript, import is a much worse notification than JSX, not to mention CV several iconnames from the import section. And add the tag…

This is it? This is it?

The old driver has been unable to restrain the inner irony, spend 1 minute to write out the first answer

// src/Icon.tsx
import React, { FC } from "react";
import * as AllIcons from "@ant-design/icons";

const Icon: FC<{ name: keyof typeof AllIcons }> = ({ name }) = > {
  const Comp = AllIcons[name] as any; // The reason for writing any here is explained in a separate article
  return <Comp />;
};

export default Icon;
Copy the code

Autumn famous mountain car god, is you! But as tactful as you are, you soon discovered emmmmm… It seems like the Icon has other attributes, so you have to waste another precious minute of your life trying to get them

/ /!!!!!! Neither IconProps nor AntdIconProps exists, so the following line will report an error
import { IconProps, AntdIconProps } from '@ant-design/icons';
Copy the code

Icon props definition, then found that, sure enough, there is no export. (╯ ‵ – ‘╯ ︵ ┻ ━ ┻

In fact, not only do @ant-design/ ICONS not export the props type, but many React component libraries do not export the props type of the component.

This… You can’t change libraries you can’t see me: patch-package Luckily we’re smart enough with typescript, we just do this and this and this and typescript infer

Let there be light

// Get the component type
type PickProps<T> = T extends (props: infer P1) => any
  ? P1
  : T extends React.ComponentClass<infer P2>
  ? P2
  : unknown;
Copy the code

We can use this tool to get the React component’s property type so we can modify it slightly

// src/Icon.tsx
import React, { FC } from "react";
import AndtIcon from "@ant-design/icons";
import * as AllIcons from "@ant-design/icons";

type PickProps<T> = T extends (props: infer P1) => any
  ? P1
  : T extends React.ComponentClass<infer P2>
  ? P2
  : unknown;

type AllKeys = keyof typeof AllIcons;
// Get exports that start with uppercase and consider them components
type PickCapitalizeAsComp<K extends AllKeys> = K extends Capitalize<K>
  ? K
  : never;
/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ^ typescript 4.1 + -- -- -- -- -- -- -- --
type IconNames = PickCapitalizeAsComp<AllKeys>;
// There are no 4.1 methods that can manually exclude lowercase characters
// type IconNames = Exclude<
// AllKeys,
// "createFromIconfontCN" | "default" | "getTwoToneColor" | "setTwoToneColor"
/ / >;

// It is no longer possible to use FC as a wrapper, for reasons that can be discussed in another article
const Icon: FC<{ name: IconNames } & PickProps<typeof AndtIcon>> = ({ name, ... props }) = > {
  const Comp = AllIcons[name] as React.ClassType<any, any, any>;
  return <Comp {. props} / >;
};

export default Icon;
Copy the code

Awesome! But wait, what about the two-color icon?? How did the twoToneColor attribute disappear?

The o… Because all I import is import AndtIcon from “@ant-design/ ICONS “; Ah!!!!!! , and the default export AntdIcon does not add this attribute, you can click on the detailed type definition to see

// src/Icon.tsx
import AntdIcon from "@ant-design/icons"; ----------------------^ Click here for the type definition ----------// node_modules/@ant-design/icons/index.d.ts
export { default } from './components/Icon'; ---------^ Click here for the type definition ----------// node_modules/@ant-design/icons/libs/components/icon.d.ts
// You can search twoToneColor and see that there is no Pick
Copy the code

This problem can actually be extended

How do we get the properties of a specified component based on its name in a specified component collection? TODO: there’s an opportunity TODO another one on apps

After all, there are several types of ICONS, and they all have different props

Awesome Typescript!

Again, we can do typescript generics like this and like this and like this

import React from "react";
import * as AllIcons from "@ant-design/icons";

type PickProps<T> = T extends (props: infer P1) => any
  ? P1
  : T extends React.ComponentClass<infer P2>
  ? P2
  : unknown;

type AllKeys = keyof typeof AllIcons;
// Get exports that start with uppercase and consider them components
type PickCapitalizeAsComp<K extends AllKeys> = K extends Capitalize<K>
  ? K
  : never;
/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ^ typescript 4.1 + -- -- -- -- -- -- -- --
type IconNames = PickCapitalizeAsComp<AllKeys>;
// There are no 4.1 methods that can manually exclude lowercase characters
// type IconNames = Exclude<
// AllKeys,
// "createFromIconfontCN" | "default" | "getTwoToneColor" | "setTwoToneColor"
/ / >;

export type PickIconPropsOf<K extends IconNames> = PickProps<
  typeof AllIcons[K]
>;

// It is no longer possible to use FC as a wrapper, for reasons that can be discussed in another article
constIcon = <T extends IconNames, P extends Object = PickIconPropsOf<T>>({ name, ... props }: { name: T } & P) => { const Comp = AllIcons[name] as React.ClassType<any, any, any>; return <Comp {... props} />; }; export default Icon;Copy the code

Leopard cat for prince

A wave of operation fierce as tiger, a look at packing 250KB; It’s like we lost the most important Tree Shaking. After all, we now import * as AllIcons from ‘@ant-design/ ICONS ‘;

Import () Dynamic import; Get away from Tree Shaking with dynamically loaded effects

finally!!

import React, { useState, useEffect } from "react";
import { LoadingOutlined } from "@ant-design/icons";
// TODO:This line should result in a full import, but we are only using types here, so the actual use is extracted into a separate D.ts file
import * as AllIcons from "@ant-design/icons";

type PickProps<T> = T extends (props: infer P1) => any
  ? P1
  : T extends React.ComponentClass<infer P2>
  ? P2
  : unknown;

type AllKeys = keyof typeof AllIcons;
// Get exports that start with uppercase and consider them components
type PickCapitalizeAsComp<K extends AllKeys> = K extends Capitalize<K>
  ? K
  : never;
/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ^ typescript 4.1 + -- -- -- -- -- -- -- --
type IconNames = PickCapitalizeAsComp<AllKeys>;
// There are no 4.1 methods that can manually exclude lowercase characters
// type IconNames = Exclude<
// AllKeys,
// "createFromIconfontCN" | "default" | "getTwoToneColor" | "setTwoToneColor"
/ / >;

export type PickIconPropsOf<K extends IconNames> = PickProps<
  typeof AllIcons[K]
>;

// It is no longer possible to use FC as a wrapper, for reasons that can be discussed in another article
constIcon = <T extends IconNames, P extends Object = PickIconPropsOf<T>>({ name, ... props }: { name: T } & Omit<P, 'name'>) => { const [Comp, setComp] = useState<React.ClassType<any, any, any>>( LoadingOutlined ); useEffect(() => { import(`@ant-design/icons/${name}.js`).then((mod) => { setComp(mod.default); }); }, [name]); return <Comp {... props} />; }; export default Icon;Copy the code

Source point I end scatter flower 🎉🎉🎉


Of course, there are other solutions to this need

  1. Can tree shaking also be done with Babel? (yes!

Time is a little rushed, many places to write in haste, welcome to leave a message to discuss or wait for me to fill the pit (escape

Typescript learning materials

  • Official: faced
  • Understand TypeScript in depth

Of course there are a lot of very high quality articles on the Nuggets as well

We are hiring too!!

Look here look here # Nuggets boiling point # juejin.cn/pin/6921602…