Skip to content

Commit

Permalink
fix(merge-styles): introduce DeepPartialV2 type to avoid infinite t…
Browse files Browse the repository at this point in the history
…ype instantiation and make unions declaration more performant (#31703)
  • Loading branch information
Hotell authored Jun 14, 2024
1 parent 57cd634 commit 53c54d4
Show file tree
Hide file tree
Showing 14 changed files with 162 additions and 148 deletions.
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": "martinhochel@microsoft.com",
"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

0 comments on commit 53c54d4

Please sign in to comment.