Skip to content

PropOr typings are not safe #559

@peterreisz

Description

@peterreisz

I gathered several cases with the results, please review them and If you agreed fix the typings according to.

interface FooBar {
  readonly required: string;
  readonly optional?: string;
}
const requiredFoobar: FooBar = {} as any;
const optionalFoobar: undefined | FooBar = {} as any; // Do not define object here, because TS use that instead of the type definitions

const returnsString = propOr('required', 'required', requiredFoobar);
const shouldNotCompile = propOr(undefined, 'required', requiredFoobar); // because the property is required, and the input object is present
const returnsString = propOr('optional', 'optional', requiredFoobar); // when we define the default value it must return with a T type
const returnsStringOrUndefined = propOr(undefined, 'optional', requiredFoobar); // when the output can be undefined than it must return a type T | undefined

const returnsString = propOr('required', 'required', optionalFoobar);
const returnsStringOrUndefined = propOr(undefined, 'required', optionalFoobar);
const returnsString = propOr('optional', 'optional', optionalFoobar);
const returnsStringOrUndefined = propOr(undefined, 'optional', optionalFoobar);

// Should not compile in case of type mismatch
const shouldNotCompile = propOr(1, 'required', requiredFoobar);
const shouldNotCompile = propOr(1, 'optional', requiredFoobar);
const shouldNotCompile = propOr(1, 'required', optionalFoobar);
const shouldNotCompile = propOr(1, 'optional', optionalFoobar);

// Should not compile in case of key is not in the object
const shouldNotCompile = propOr('', 'foo', requiredFoobar);
const shouldNotCompile = propOr('', 'foo', optionalFoobar);

I also tried to fix the type definition, it is the best I could do:

type PropOrValue<T, K extends keyof NonNullable<T>> = T extends NonNullable<T> ? NonNullable<T>[K] : NonNullable<T>[K] | undefined

/**
 * It returns either `defaultValue` or the value of `property` in `obj`.
 */
export function propOr<T, K extends keyof NonNullable<T>>(defaultValue: NonNullable<NonNullable<T>[K]>, property: K, obj: T): NonNullable<NonNullable<T>[K]>;
export function propOr<T, K extends keyof NonNullable<T>>(defaultValue: PropOrValue<T, K>, property: K, obj: T): PropOrValue<T, K>;

export function propOr<T, K extends keyof NonNullable<T>>(defaultValue: NonNullable<NonNullable<T>[K]>, property: K): (obj: T) => NonNullable<NonNullable<T>[K]>;
export function propOr<T, K extends keyof NonNullable<T>>(defaultValue: PropOrValue<T, K>, property: K): (obj: T) => PropOrValue<T, K>;

export function propOr<T, K extends keyof NonNullable<T>>(defaultValue: NonNullable<NonNullable<T>[K]>): FunctionToolbelt.Curry<(property: K, obj: T) => NonNullable<NonNullable<T>[K]>>;
export function propOr<T, K extends keyof NonNullable<T>>(defaultValue: PropOrValue<T, K>): FunctionToolbelt.Curry<(property: K, obj: T) => PropOrValue<T, K>>;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions