woutervh- / typescript-is

MIT License
959 stars 35 forks source link

Allow at least typeof/instanceof checks for basic methods/functions #52

Open timse opened 4 years ago

timse commented 4 years ago

I know you mentioned before that this strives to only work for serializable data.

However, 😏 would it be possible, happily hidden behind an options flag, to not simply ignore methods/functions but to allow at least a simple type check of sorts, to at least make the minimal guarantee that the provided value is actually a function and not e.g. a number?

woutervh- commented 4 years ago

Hi @timse

Yes, it's possible to add a check for methods and function, for example to generate typeof X === 'function'. May I know more about your use case? I'm wondering if many people need this functionality or not.

bradennapier commented 4 years ago

This would be ideal. As it stands it seems to fail in this case

import { is } from 'typescript-is';

type MyType = {
  one: string;
  two: number;
};

type MyTypeFn = {
  two: () => any;
};

function run(obj?: undefined | MyType | Partial<MyType> | MyTypeFn) {
  if (is<MyType>(obj)) {
    return obj.one;
  }
  if (is<MyTypeFn>(obj)) {
    return obj.two;
  }
}

console.log(run({ one: 'hi' }));
console.log(run({ one: 'hi', two: 2 }));
console.log(run({ two: () => console.log('yep') }));
console.log(run({}));

with the results:

undefined
hi
undefined
undefined

I would expect that the is<MyTypeFn> to mean the response here would be the function itself. but it is returning undefined. I have enabled ignoreFunctions which it said would just return true here... but either way it makes sense that if the checker finds a function type in the type it is checking for it to at least just do a typeof value === 'function' check and have a flag to enable or disable.

It is less than ideal that any type that happens to have a function in it makes this fail completely.

Other than that this is an absolutely awesome module and thank you!

Venryx commented 4 years ago

I have the same request.

My use case is that I am doing runtime-type-checking of all arguments passed between the frontend and backend of an Electron. app (the data passes through a context-bridge, since I have context-isolation turned on)

I do these generic runtime-type-checks as an extra precaution, in case at some point the frontend portion of my app became compromised; the idea is to limit the chances the frontend could perform harmful effects on the NodeJS side, by limiting its options for crafting unexpected arguments that would trick the backend functions into performing harmful actions. (higher-level vulnerabilities I already handle through custom assertions)

Because these are just precautionary runtime-type-checks, I cannot show clear examples of cases where having an "is function" check is important. However, every bit that you can "tighten the requirements", is a win for constraining the manipulation options of the theoretically-compromised frontend.

EDIT: In response to the possible question: "Why wouldn't someone just write typeof argX == function checks manually, instead of is<Function>(argX) -- since it's about the same length?"

Well, one reason is that it's nice to have all parameters using the same "@AssertType()" decorators for enacting the type-checking.

The other reason is that some of the functions also receive objects matching nested interface types. With @AssertType(), I get automatic type-checking of the whole interface/object-tree (regardless of depth of the function property); whereas if I used manual typeof checks for them, I would have to recreate the type-checking tree. (or somehow "extract out" the nested function property, eg. setting it to any within the interface structure, and creating manual tunnel-down-and-assert code for those properties)

woutervh- commented 3 years ago

hi everyone,

I've added a transformer option functionBehavior in v0.17.0: https://github.com/woutervh-/typescript-is/releases/tag/v0.17.0

This option can take the values basic, ignore or error. error is like ignoreFunctions: false, it will throw compile-time errors when functions are found. ignore is like ignoreFunctions: true, it will create a validation which always passes. basic is new, and it will create a validation which does a minimal typeof X === 'function' check.

Hope this helps you with functions.

Regarding classes, I'm still not decided on what the best approach is, but feel free to join the discussion here #20