Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: replace slow Omit type #4068

Merged
merged 4 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 7 additions & 11 deletions packages/styled-components/src/models/StyledComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ let seenUnknownProps = new Set();
function useStyledComponentImpl<Props extends object>(
forwardedComponent: IStyledComponent<'web', Props>,
props: ExecutionProps & Props,
forwardedRef: Ref<Element>,
isStatic: boolean
forwardedRef: Ref<Element>
) {
const {
attrs: componentAttrs,
Expand Down Expand Up @@ -164,7 +163,7 @@ function useStyledComponentImpl<Props extends object>(

const generatedClassName = useInjectedStyle(componentStyle, context);

if (process.env.NODE_ENV !== 'production' && !isStatic && forwardedComponent.warnTooManyClasses) {
if (process.env.NODE_ENV !== 'production' && forwardedComponent.warnTooManyClasses) {
forwardedComponent.warnTooManyClasses(generatedClassName);
}

Expand Down Expand Up @@ -242,22 +241,19 @@ function createStyledComponent<
isTargetStyledComp ? (styledComponentTarget.componentStyle as ComponentStyle) : undefined
);

// statically styled-components don't need to build an execution context object,
// and shouldn't be increasing the number of class names
const isStatic = componentStyle.isStatic && attrs.length === 0;
function forwardRef(props: ExecutionProps & OuterProps, ref: Ref<Element>) {
return useStyledComponentImpl<OuterProps>(WrappedStyledComponent, props, ref, isStatic);
function forwardRefRender(props: ExecutionProps & OuterProps, ref: Ref<Element>) {
return useStyledComponentImpl<OuterProps>(WrappedStyledComponent, props, ref);
}

forwardRef.displayName = displayName;
forwardRefRender.displayName = displayName;

/**
* forwardRef creates a new interim component, which we'll take advantage of
* instead of extending ParentComponent to create _another_ interim class
*/
let WrappedStyledComponent = React.forwardRef(forwardRef) as unknown as IStyledComponent<
let WrappedStyledComponent = React.forwardRef(forwardRefRender) as unknown as IStyledComponent<
'web',
OuterProps
any
> &
Statics;
WrappedStyledComponent.attrs = finalAttrs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export default (InlineStyle: IInlineStyleConstructor<any>) => {
*/
let WrappedStyledComponent = React.forwardRef(forwardRef) as unknown as IStyledComponent<
'native',
OuterProps
any
> &
Statics;

Expand Down
4 changes: 3 additions & 1 deletion packages/styled-components/src/sheet/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import getNonce from '../utils/nonce';

/** Find last style element if any inside target */
const findLastStyleTag = (target: HTMLElement): void | HTMLStyleElement => {
return Array.from(target.querySelectorAll<HTMLStyleElement>(`style[${SC_ATTR}]`)).at(-1);
const arr = Array.from(target.querySelectorAll<HTMLStyleElement>(`style[${SC_ATTR}]`));

return arr[arr.length - 1];
};

/** Create a style element inside `target` or <head> after the last */
Expand Down
18 changes: 12 additions & 6 deletions packages/styled-components/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import createWarnTooManyClasses from './utils/createWarnTooManyClasses';

export { CSS, DefaultTheme };

interface ExoticComponentWithDisplayName<P = any> extends React.ExoticComponent<P> {
interface ExoticComponentWithDisplayName<P extends object = {}> extends React.ExoticComponent<P> {
defaultProps?: Partial<P>;
displayName?: string;
}
Expand All @@ -22,9 +22,15 @@ export type BaseObject = {};
// from https://stackoverflow.com/a/69852402
export type OmitNever<T> = { [K in keyof T as T[K] extends never ? never : K]: T[K] };

type FastOmit<T extends object, U extends string | number | symbol> = {
[K in keyof T as K extends U ? never : K]: T[K];
};

export type Runtime = 'web' | 'native';

export type AnyComponent<P = any> = ExoticComponentWithDisplayName<P> | React.ComponentType<P>;
export type AnyComponent<P extends object = any> =
| ExoticComponentWithDisplayName<P>
| React.ComponentType<P>;

export type KnownTarget = Exclude<keyof JSX.IntrinsicElements, 'symbol' | 'object'> | AnyComponent;

Expand Down Expand Up @@ -161,15 +167,15 @@ export type PolymorphicComponentProps<
ForwardedAsTargetProps extends object = ForwardedAsTarget extends KnownTarget
? React.ComponentPropsWithoutRef<ForwardedAsTarget>
: {}
> = Omit<
> = FastOmit<
Substitute<
BaseProps,
// "as" wins over "forwardedAs" when it comes to prop interface
Substitute<ForwardedAsTargetProps, AsTargetProps>
>,
keyof ExecutionProps
> &
Omit<ExecutionProps, 'as' | 'forwardedAs'> & {
FastOmit<ExecutionProps, 'as' | 'forwardedAs'> & {
as?: AsTarget;
forwardedAs?: ForwardedAsTarget;
};
Expand All @@ -195,7 +201,7 @@ export interface IStyledComponent<R extends Runtime, Props extends object = Base
extends PolymorphicComponent<R, Props>,
IStyledStatics<R, Props>,
StyledComponentBrand {
defaultProps?: Partial<Substitute<ExecutionProps, Props>>;
defaultProps?: ExecutionProps & Partial<Props>;
toString: () => string;
}

Expand Down Expand Up @@ -259,4 +265,4 @@ export type CSSProp = Interpolation<any>;
// Prevents TypeScript from inferring generic argument
export type NoInfer<T> = [T][T extends any ? 0 : never];

export type Substitute<A extends object, B extends object> = Omit<A, keyof B> & B;
export type Substitute<A extends object, B extends object> = FastOmit<A, keyof B> & B;