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

Proposal: validate type of object properties #141

Open
lo1tuma opened this issue Sep 27, 2021 · 4 comments
Open

Proposal: validate type of object properties #141

lo1tuma opened this issue Sep 27, 2021 · 4 comments

Comments

@lo1tuma
Copy link

lo1tuma commented Sep 27, 2021

I would like to propose a new function is.propertyOf(object: unknown: key: string, predicate: Predicate): boolean that accepts 3 values and checks:

  1. wether the given object value is an object and
  2. if the given object has an own property with the name key and
  3. that the value of the specified property matches the given predicate

Example Usage:

is.propertyOf(foo, 'bar', is.string);

Why

Given the following example

interface Foo {
  bar?: string;
  baz: number
}

function doStuff(foo?: Foo) {
  if (is.string(foo?.bar)) { // using optional chaining because `foo` might be undefined
    console.log(foo.baz); // typescript still things `foo` could be undefined
  }
}

Unfortunately typescript is not smart enough to understand that within the if block foo is always defined.

Let’s say is.propertyOf is implemented similar to this:

function propertyOf<O extends unknown, K extends keyof Exclude<O, undefined>, P>(obj: O, key: K, predicate: Predicate<P>): obj is Exclude<O, undefined> & Record<K, P> {
  return true;
} 

then the code from above could look like this:

interface Foo {
  bar?: string;
  baz: number
}

function doStuff(foo?: Foo) {
  if (is.propertyOf(foo, 'bar', t.string)) {
    console.log(foo.bar); // typescript now knows two things: foo is an object and its property bar is a string
  }
}
@sindresorhus
Copy link
Owner

Are you aware of any TypeScript issues about it? Would you be able to link some?

From a quick search, microsoft/TypeScript#38839 looks relevant.

@lo1tuma
Copy link
Author

lo1tuma commented Sep 27, 2021

I haven’t really checked the TypeScript issues before. It looks like that microsoft/TypeScript#38839 could fix this problem. I’ve also found this issue microsoft/TypeScript#34974.

@younho9
Copy link

younho9 commented Dec 14, 2021

It seems likely to be extended to a function that verify the schema of an object.

Some ideas are as follows.

import is from '@sindresorhus/is'
import {objectEntries, objectHasOwn} from 'ts-extras';

const isInterface = <ObjectType extends Record<string, unknown>>(
  value: unknown,
  interface_: {
    [Key in keyof ObjectType]: (value: unknown) => value is ObjectType[Key];
  },
): value is ObjectType => {
  return objectEntries(interface_).every(
    ([key, predicate]) => objectHasOwn(value, key) && predicate(value[key]),
  );
};

declare const someObject: unknown;

if (
  isInterface(someObject, {
    foo: is.string,
    bar: is.number,
    baz: is.boolean,
  })
) {
  someObject;
  // {
  //     foo: string;
  //     bar: number;
  //     baz: boolean;
  // }
}

TypeScript Playground

@younho9
Copy link

younho9 commented Dec 15, 2021

There is a similar implementation in ow. sindresorhus/ow#92

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants