piotrwitek / utility-types

Collection of utility types, complementing TypeScript built-in mapped types and aliases (think "lodash" for static types).
MIT License
5.54k stars 230 forks source link

DeepPartial does not allow empty object for generics #183

Closed mpiniarski closed 1 year ago

mpiniarski commented 1 year ago

Description

When the DeepPartial generic argument is generic itself, the resulting type does now allow {} as a value.

Steps to Reproduce

Given the code:

import type { DeepPartial } from "utility-types";

const test = <T extends object>() => {
    const result: DeepPartial<T> = {} //  ❌ type error, "TS2322: Type '{}' is not assignable to type 'DeepPartial'."
    return result;
}

even though it works well for

const result: DeepPartial<{ test: string }> = {}

Typescript cannot limit conditional type in DeepPartial to the case when T extends object even though the passed parameter is declared this way.

Expected behavior

const result: DeepPartial<T> = {}

does not cause a type error.

Suggested solution(s)

The exported DeepPartial should work on objects.

export declare type DeepPartial<T> = {
    [P in keyof T]?: _DeepPartial<T[P]>;
};
/** @private */
export declare type _DeepPartial<T> = T extends Function ? T : T extends Array<infer U> ? _DeepPartialArray<U> : T extends object ? _DeepPartialObject<T> : T | undefined;
/** @private */
export interface _DeepPartialArray<T> extends Array<_DeepPartial<T>> {
}

Project Dependencies

mpiniarski commented 1 year ago

@piotrwitek I'll submit a PR.

piotrwitek commented 1 year ago

Hej @mpiniarski yes feel free to do it, thanks :) Could you also try to add some new test cases to cover that type bug?