Skip to content

fix(merge-styles): introduce DeepPartialV2 type to avoid infinite type instantiation and make unions declaration more performant #31703

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

Merged
merged 6 commits into from
Jun 14, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix(merge-styles): improve DeepPartial type infinite recursion triggerpoint",
"packageName": "@fluentui/merge-styles",
"email": "[email protected]",
"dependentChangeType": "patch"
}
88 changes: 48 additions & 40 deletions packages/merge-styles/etc/merge-styles.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,39 @@ export type AddSheetCallback = ({ key, sheet }: {
// @public (undocumented)
export const cloneCSSStyleSheet: (srcSheet: CSSStyleSheet, targetSheet: CSSStyleSheet) => CSSStyleSheet;

// Warning: (ae-forgotten-export) The symbol "Missing_3" needs to be exported by the entry point index.d.ts
//
// @public
export function concatStyleSets<TStyleSet>(styleSet: TStyleSet | false | null | undefined): IConcatenatedStyleSet<ObjectOnly<TStyleSet>>;
export function concatStyleSets<TStyleSet>(styleSet: TStyleSet | Missing_3): IConcatenatedStyleSet<ObjectOnly<TStyleSet>>;

// Warning: (ae-forgotten-export) The symbol "MissingOrShadowConfig_2" needs to be exported by the entry point index.d.ts
//
// @public
export function concatStyleSets<TStyleSet1, TStyleSet2>(styleSet1: TStyleSet1 | false | null | undefined | ShadowConfig, styleSet2: TStyleSet2 | false | null | undefined): IConcatenatedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2>>;
export function concatStyleSets<TStyleSet1, TStyleSet2>(styleSet1: TStyleSet1 | MissingOrShadowConfig_2, styleSet2: TStyleSet2 | Missing_3): IConcatenatedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2>>;

// @public
export function concatStyleSets<TStyleSet1, TStyleSet2, TStyleSet3>(styleSet1: TStyleSet1 | false | null | undefined | ShadowConfig, styleSet2: TStyleSet2 | false | null | undefined, styleSet3: TStyleSet3 | false | null | undefined): IConcatenatedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3>>;
export function concatStyleSets<TStyleSet1, TStyleSet2, TStyleSet3>(styleSet1: TStyleSet1 | MissingOrShadowConfig_2, styleSet2: TStyleSet2 | Missing_3, styleSet3: TStyleSet3 | Missing_3): IConcatenatedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3>>;

// @public
export function concatStyleSets<TStyleSet1, TStyleSet2, TStyleSet3, TStyleSet4>(styleSet1: TStyleSet1 | false | null | undefined | ShadowConfig, styleSet2: TStyleSet2 | false | null | undefined, styleSet3: TStyleSet3 | false | null | undefined, styleSet4: TStyleSet4 | false | null | undefined): IConcatenatedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3> & ObjectOnly<TStyleSet4>>;
export function concatStyleSets<TStyleSet1, TStyleSet2, TStyleSet3, TStyleSet4>(styleSet1: TStyleSet1 | MissingOrShadowConfig_2, styleSet2: TStyleSet2 | Missing_3, styleSet3: TStyleSet3 | Missing_3, styleSet4: TStyleSet4 | Missing_3): IConcatenatedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3> & ObjectOnly<TStyleSet4>>;

// @public
export function concatStyleSets<TStyleSet1, TStyleSet2, TStyleSet3, TStyleSet4, TStyleSet5>(styleSet1: TStyleSet1 | false | null | undefined | ShadowConfig, styleSet2: TStyleSet2 | false | null | undefined, styleSet3: TStyleSet3 | false | null | undefined, styleSet4: TStyleSet4 | false | null | undefined, styleSet5: TStyleSet5 | false | null | undefined): IConcatenatedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3> & ObjectOnly<TStyleSet4> & ObjectOnly<TStyleSet5>>;
export function concatStyleSets<TStyleSet1, TStyleSet2, TStyleSet3, TStyleSet4, TStyleSet5>(styleSet1: TStyleSet1 | MissingOrShadowConfig_2, styleSet2: TStyleSet2 | Missing_3, styleSet3: TStyleSet3 | Missing_3, styleSet4: TStyleSet4 | Missing_3, styleSet5: TStyleSet5 | Missing_3): IConcatenatedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3> & ObjectOnly<TStyleSet4> & ObjectOnly<TStyleSet5>>;

// @public
export function concatStyleSets<TStyleSet1, TStyleSet2, TStyleSet3, TStyleSet4, TStyleSet5, TStyleSet6>(styleSet1: TStyleSet1 | false | null | undefined | ShadowConfig, styleSet2: TStyleSet2 | false | null | undefined, styleSet3: TStyleSet3 | false | null | undefined, styleSet4: TStyleSet4 | false | null | undefined, styleSet5: TStyleSet5 | false | null | undefined, styleSet6: TStyleSet6 | false | null | undefined): IConcatenatedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3> & ObjectOnly<TStyleSet4> & ObjectOnly<TStyleSet5> & ObjectOnly<TStyleSet6>>;
export function concatStyleSets<TStyleSet1, TStyleSet2, TStyleSet3, TStyleSet4, TStyleSet5, TStyleSet6>(styleSet1: TStyleSet1 | MissingOrShadowConfig_2, styleSet2: TStyleSet2 | Missing_3, styleSet3: TStyleSet3 | Missing_3, styleSet4: TStyleSet4 | Missing_3, styleSet5: TStyleSet5 | Missing_3, styleSet6: TStyleSet6 | Missing_3): IConcatenatedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3> & ObjectOnly<TStyleSet4> & ObjectOnly<TStyleSet5> & ObjectOnly<TStyleSet6>>;

// @public
export function concatStyleSets(...styleSets: (IStyleSet | false | null | undefined | ShadowConfig)[]): IConcatenatedStyleSet<any>;
export function concatStyleSets(...styleSets: (IStyleSet | MissingOrShadowConfig_2)[]): IConcatenatedStyleSet<any>;

// Warning: (ae-forgotten-export) The symbol "DeepPartialV2" needs to be exported by the entry point index.d.ts
//
// @public
export function concatStyleSetsWithProps<TStyleProps, TStyleSet extends IStyleSetBase>(styleProps: TStyleProps, ...allStyles: (IStyleFunctionOrObject<TStyleProps, TStyleSet> | undefined)[]): DeepPartial<TStyleSet>;
export function concatStyleSetsWithProps<TStyleProps, TStyleSet extends IStyleSetBase>(styleProps: TStyleProps, ...allStyles: (IStyleFunctionOrObject<TStyleProps, TStyleSet> | undefined)[]): DeepPartialV2<TStyleSet>;

// @public
// @public @deprecated
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[] ? DeepPartial<U>[] : T[P] extends object ? DeepPartial<T[P]> : T[P];
[P in keyof T]?: T[P] extends Array<infer U> ? Array<DeepPartial<U>> : T[P] extends object ? DeepPartial<T[P]> : T[P];
};

// @public (undocumented)
Expand Down Expand Up @@ -453,10 +459,10 @@ export interface IStyleBaseArray extends Array<IStyle> {
}

// @public
export type IStyleFunction<TStylesProps, TStyleSet extends IStyleSetBase> = (props: TStylesProps) => DeepPartial<TStyleSet>;
export type IStyleFunction<TStylesProps, TStyleSet extends IStyleSetBase> = (props: TStylesProps) => DeepPartialV2<TStyleSet>;

// @public
export type IStyleFunctionOrObject<TStylesProps, TStyleSet extends IStyleSetBase> = IStyleFunction<TStylesProps, TStyleSet> | DeepPartial<TStyleSet>;
export type IStyleFunctionOrObject<TStylesProps, TStyleSet extends IStyleSetBase> = IStyleFunction<TStylesProps, TStyleSet> | DeepPartialV2<TStyleSet>;

// @public
export type IStyleSet<TStyleSet extends IStyleSetBase = {
Expand Down Expand Up @@ -503,58 +509,56 @@ export function keyframes(timeline: IKeyframes): string;
// @public (undocumented)
export const makeShadowConfig: (stylesheetKey: string, inShadow: boolean, window?: Window) => ShadowConfig;

// Warning: (ae-forgotten-export) The symbol "StyleArgWithShadow" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "IStyleOptions" needs to be exported by the entry point index.d.ts
//
// @public
export function mergeCss(args: (IStyle | IStyleBaseArray | false | null | undefined | ShadowConfig) | (IStyle | IStyleBaseArray | false | null | undefined | ShadowConfig)[], options?: IStyleOptions): string;
export function mergeCss(args: StyleArgWithShadow | StyleArgWithShadow[], options?: IStyleOptions): string;

// Warning: (ae-forgotten-export) The symbol "Missing_2" needs to be exported by the entry point index.d.ts
//
// @public
export function mergeCssSets<TStyleSet>(styleSets: [TStyleSet | false | null | undefined], options?: IStyleOptions): IProcessedStyleSet<ObjectOnly<TStyleSet>>;
export function mergeCssSets<TStyleSet>(styleSets: [TStyleSet | Missing_2], options?: IStyleOptions): IProcessedStyleSet<ObjectOnly<TStyleSet>>;

// Warning: (ae-forgotten-export) The symbol "MissingOrShadowConfig" needs to be exported by the entry point index.d.ts
//
// @public
export function mergeCssSets<TStyleSet1, TStyleSet2>(styleSets: [TStyleSet1 | false | null | undefined | ShadowConfig, TStyleSet2 | false | null | undefined], options?: IStyleOptions): IProcessedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2>>;
export function mergeCssSets<TStyleSet1, TStyleSet2>(styleSets: [TStyleSet1 | MissingOrShadowConfig, TStyleSet2 | Missing_2], options?: IStyleOptions): IProcessedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2>>;

// @public
export function mergeCssSets<TStyleSet1, TStyleSet2, TStyleSet3>(styleSets: [
TStyleSet1 | false | null | undefined | ShadowConfig,
TStyleSet2 | false | null | undefined,
TStyleSet3 | false | null | undefined
], options?: IStyleOptions): IProcessedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3>>;
export function mergeCssSets<TStyleSet1, TStyleSet2, TStyleSet3>(styleSets: [TStyleSet1 | MissingOrShadowConfig, TStyleSet2 | Missing_2, TStyleSet3 | Missing_2], options?: IStyleOptions): IProcessedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3>>;

// @public
export function mergeCssSets<TStyleSet1, TStyleSet2, TStyleSet3, TStyleSet4>(styleSets: [
TStyleSet1 | false | null | undefined | ShadowConfig,
TStyleSet2 | false | null | undefined,
TStyleSet3 | false | null | undefined,
TStyleSet4 | false | null | undefined
], options?: IStyleOptions): IProcessedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3> & ObjectOnly<TStyleSet4>>;
export function mergeCssSets<TStyleSet1, TStyleSet2, TStyleSet3, TStyleSet4>(styleSets: [TStyleSet1 | MissingOrShadowConfig, TStyleSet2 | Missing_2, TStyleSet3 | Missing_2, TStyleSet4 | Missing_2], options?: IStyleOptions): IProcessedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3> & ObjectOnly<TStyleSet4>>;

// @public
export function mergeCssSets<TStyleSet>(styleSet: [TStyleSet | false | null | undefined], options?: IStyleOptions): IProcessedStyleSet<ObjectOnly<TStyleSet>>;
export function mergeCssSets<TStyleSet>(styleSet: [TStyleSet | Missing_2], options?: IStyleOptions): IProcessedStyleSet<ObjectOnly<TStyleSet>>;

// Warning: (ae-forgotten-export) The symbol "StyleArg" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export function mergeStyles(...args: (IStyle | IStyleBaseArray | false | null | undefined)[]): string;
export function mergeStyles(...args: StyleArg[]): string;

// @public (undocumented)
export function mergeStyles(shadowConfig: ShadowConfig, ...args: (IStyle | IStyleBaseArray | false | null | undefined)[]): string;
export function mergeStyles(shadowConfig: ShadowConfig, ...args: StyleArg[]): string;

// @public
export function mergeStyleSets<TStyleSet>(styleSet: TStyleSet | false | null | undefined): IProcessedStyleSet<ObjectOnly<TStyleSet>>;
export function mergeStyleSets<TStyleSet>(styleSet: TStyleSet | Missing_2): IProcessedStyleSet<ObjectOnly<TStyleSet>>;

// @public
export function mergeStyleSets<TStyleSet1, TStyleSet2>(styleSet1: TStyleSet1 | false | null | undefined, styleSet2: TStyleSet2 | false | null | undefined): IProcessedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2>>;
export function mergeStyleSets<TStyleSet1, TStyleSet2>(styleSet1: TStyleSet1 | Missing_2, styleSet2: TStyleSet2 | Missing_2): IProcessedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2>>;

// @public
export function mergeStyleSets<TStyleSet1, TStyleSet2, TStyleSet3>(styleSet1: TStyleSet1 | false | null | undefined, styleSet2: TStyleSet2 | false | null | undefined, styleSet3: TStyleSet3 | false | null | undefined): IProcessedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3>>;
export function mergeStyleSets<TStyleSet1, TStyleSet2, TStyleSet3>(styleSet1: TStyleSet1 | Missing_2, styleSet2: TStyleSet2 | Missing_2, styleSet3: TStyleSet3 | Missing_2): IProcessedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3>>;

// @public
export function mergeStyleSets<TStyleSet1, TStyleSet2, TStyleSet3, TStyleSet4>(styleSet1: TStyleSet1 | false | null | undefined, styleSet2: TStyleSet2 | false | null | undefined, styleSet3: TStyleSet3 | false | null | undefined, styleSet4: TStyleSet4 | false | null | undefined): IProcessedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3> & ObjectOnly<TStyleSet4>>;
export function mergeStyleSets<TStyleSet1, TStyleSet2, TStyleSet3, TStyleSet4>(styleSet1: TStyleSet1 | Missing_2, styleSet2: TStyleSet2 | Missing_2, styleSet3: TStyleSet3 | Missing_2, styleSet4: TStyleSet4 | Missing_2): IProcessedStyleSet<ObjectOnly<TStyleSet1> & ObjectOnly<TStyleSet2> & ObjectOnly<TStyleSet3> & ObjectOnly<TStyleSet4>>;

// @public
export function mergeStyleSets(...styleSets: Array<IStyleSet | undefined | false | null | ShadowConfig>): IProcessedStyleSet<any>;
export function mergeStyleSets(...styleSets: Array<IStyleSet | MissingOrShadowConfig>): IProcessedStyleSet<any>;

// @public (undocumented)
export function mergeStyleSets(shadowConfig: ShadowConfig, ...styleSets: Array<IStyleSet | undefined | false | null>): IProcessedStyleSet<any>;
export function mergeStyleSets(shadowConfig: ShadowConfig, ...styleSets: Array<IStyleSet | Missing_2>): IProcessedStyleSet<any>;

// @public (undocumented)
export type ObjectOnly<TArg> = TArg extends {} ? TArg : {};
Expand All @@ -569,12 +573,16 @@ export { Omit_2 as Omit }
export function setRTL(isRTL: boolean): void;

// @public (undocumented)
export type ShadowConfig = {
stylesheetKey: string;
export interface ShadowConfig {
// (undocumented)
__isShadowConfig__: true;
// (undocumented)
inShadow: boolean;
// (undocumented)
stylesheetKey: string;
// (undocumented)
window?: Window;
__isShadowConfig__: true;
};
}

// @public (undocumented)
export class ShadowDomStylesheet extends Stylesheet {
Expand Down Expand Up @@ -634,7 +642,7 @@ export const SUPPORTS_MODIFYING_ADOPTED_STYLESHEETS: boolean;

// Warnings were encountered during analysis:
//
// lib/IStyleSet.d.ts:61:5 - (ae-forgotten-export) The symbol "__MapToFunctionType" needs to be exported by the entry point index.d.ts
// lib/IStyleSet.d.ts:62:5 - (ae-forgotten-export) The symbol "__MapToFunctionType" needs to be exported by the entry point index.d.ts

// (No @packageDocumentation comment for this package)

Expand Down
18 changes: 17 additions & 1 deletion packages/merge-styles/src/DeepPartial.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
/**
* TypeScript type to return a deep partial object (each property can be undefined, recursively.)
* @deprecated - This type will hit infinite type instantiation recursion. Please use {@link DeepPartialV2}
*/
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[] ? DeepPartial<U>[] : T[P] extends object ? DeepPartial<T[P]> : T[P];
// eslint-disable-next-line deprecation/deprecation
[P in keyof T]?: T[P] extends Array<infer U> ? Array<DeepPartial<U>> : T[P] extends object ? DeepPartial<T[P]> : T[P];
};

interface IDeepPartialArray<T> extends Array<DeepPartialV2<T>> {}

type DeepPartialObject<T> = {
[Key in keyof T]?: DeepPartialV2<T[Key]>;
};

export type DeepPartialV2<T> = T extends Function
? T
: T extends Array<infer U>
? IDeepPartialArray<U>
: T extends object
? DeepPartialObject<T>
: T;
4 changes: 2 additions & 2 deletions packages/merge-styles/src/IRawStyle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IRawStyleBase } from './IRawStyleBase';
import { IStyle } from './IStyle';
import type { IRawStyleBase } from './IRawStyleBase';
import type { IStyle } from './IStyle';

/**
* IRawStyle extends a raw style object, but allows selectors to be defined
Expand Down
2 changes: 1 addition & 1 deletion packages/merge-styles/src/IStyle.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IRawStyle } from './IRawStyle';
import type { IRawStyle } from './IRawStyle';

/**
* {@docCategory IStyleBase}
Expand Down
2 changes: 1 addition & 1 deletion packages/merge-styles/src/IStyleFunction.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IStyleSetBase } from './IStyleSet';
import { DeepPartial } from './DeepPartial';
import type { DeepPartialV2 as DeepPartial } from './DeepPartial';

/**
* A style function takes in styleprops and returns a partial styleset.
Expand Down
2 changes: 1 addition & 1 deletion packages/merge-styles/src/IStyleOptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ShadowConfig } from './shadowConfig';
import type { ShadowConfig } from './shadowConfig';
import type { Stylesheet } from './Stylesheet';

export interface IStyleOptions {
Expand Down
12 changes: 7 additions & 5 deletions packages/merge-styles/src/IStyleSet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IStyle } from './IStyle';
import { IStyleFunctionOrObject, IStyleFunction } from './IStyleFunction';
import { ShadowConfig } from './shadowConfig';
import type { ShadowConfig } from './shadowConfig';

/**
* @deprecated Use `Exclude` provided by TypeScript instead.
Expand All @@ -12,8 +12,10 @@ export type Diff<T extends keyof any, U extends keyof any> = ({ [P in T]: P } &
/**
* @deprecated Use the version provided by TypeScript instead.
*/
// eslint-disable-next-line deprecation/deprecation, @typescript-eslint/naming-convention
type _Omit<U, K extends keyof U> = Pick<U, Diff<keyof U, K>>;
// eslint-disable-next-line deprecation/deprecation
export type Omit<U, K extends keyof U> = Pick<U, Diff<keyof U, K>>;
export type { _Omit as Omit };

/**
* Helper function whose role is supposed to express that regardless if T is a style object or style function,
Expand All @@ -39,7 +41,7 @@ export interface IStyleSetBase {
*/
export type IStyleSet<TStyleSet extends IStyleSetBase = { [key: string]: any }> = {
// eslint-disable-next-line deprecation/deprecation
[P in keyof Omit<TStyleSet, 'subComponentStyles'>]: IStyle;
[P in keyof _Omit<TStyleSet, 'subComponentStyles'>]: IStyle;
} & {
subComponentStyles?: { [P in keyof TStyleSet['subComponentStyles']]: IStyleFunctionOrObject<any, any> };
} & IShadowConfig;
Expand All @@ -49,7 +51,7 @@ export type IStyleSet<TStyleSet extends IStyleSetBase = { [key: string]: any }>
*/
export type IConcatenatedStyleSet<TStyleSet extends IStyleSetBase> = {
// eslint-disable-next-line deprecation/deprecation
[P in keyof Omit<TStyleSet, 'subComponentStyles'>]: IStyle;
[P in keyof _Omit<TStyleSet, 'subComponentStyles'>]: IStyle;
} & {
subComponentStyles?: { [P in keyof TStyleSet['subComponentStyles']]: IStyleFunction<any, any> };
} & IShadowConfig;
Expand All @@ -60,7 +62,7 @@ export type IConcatenatedStyleSet<TStyleSet extends IStyleSetBase> = {
*/
export type IProcessedStyleSet<TStyleSet extends IStyleSetBase> = {
// eslint-disable-next-line deprecation/deprecation
[P in keyof Omit<TStyleSet, 'subComponentStyles'>]: string;
[P in keyof _Omit<TStyleSet, 'subComponentStyles'>]: string;
} & {
subComponentStyles: {
[P in keyof TStyleSet['subComponentStyles']]: __MapToFunctionType<
Expand Down
Loading
Loading