0

Refer to the snippet of code I have below:

// I'm a function that does not care what the type of the argument passed through to me is
const function1 = (parameter: any) => parameter;

// I'm a function that REQUIRES a number to function
const function2 = (parameter: number) => parameter + 5;

// I'm a function that REQUIRES a string to function
const function3 = (parameter: string) => parameter.split(':');

// I'm some combinatorial function that takes in a list of functions, and returns a "meta function"
function functionAggregator<A1, R1, R2>(f1: (a1: A1) => R1, f2: (a1: A1) => R2): (a1: A1) => [R1, R2] {
  return (a1: A1) => [f1(a1), f2(a1)];
}

const validMetaFunction = functionAggregator(function1, function2);
// Valid because parameters of type number and any can overlap

const invalidMetaFunction = functionAggregator(function2, function3);
// Invalid because parameters of type number and string cannot overlap



const validFunctionResult = validMetaFunction(5);
// Valid result, because we passed in a number

const invalidFunctionResult = validMetaFunction('string');
// SHOULD be marked as an error, since type string cannot be passed into function2, which requires a type of number

On the very last line, I would expect validMetaFunction('string'); to be marked as invalid code, but because function1 has a parameter of type any, the metaFunction's parameter type is widened to be of type any. . .

Is there any way to solve this problem?

Whelch
  • 2,003
  • 2
  • 11
  • 14
  • Don't use `any`? Does `unknown` work for you instead? – jcalz Sep 24 '20 at 15:17
  • Possible duplicate of [Type intersections using any](https://stackoverflow.com/questions/46673558/type-intersections-using-any) – jcalz Sep 24 '20 at 15:19
  • 1
    @jcalz I've read that question, though what I'm asking is more related to generics: I'm *aware* that an intersection with type `any` will widen to type `any`, but I'm wondering if I can use the magic of generics to *infer* the sister function's type, and use it instead? Not specifying a type for function1 will default to any. Specifying it as type `unknown` has the same widening issue as type `any` - metaFunction's parameter is type `unknown`, so strings can still be passed in. – Whelch Sep 24 '20 at 15:24
  • Is your code a [mcve]? When [I test it](https://tsplay.dev/vWkkDW) I see the results you seem to expect/want. What, specifically, is going wrong? – jcalz Sep 24 '20 at 15:29
  • It is a minimal reproducible example. I *want* my IDE to infer that passing in a string to `validMetaFunction` will result in an error. I *want* it to look like this: https://imgur.com/a/NqZ2Q0d. Note that in order to make it show as an error, I had to change `function1` to be of type `number` (not `any`) -- when `function1` is of type `any` (like I have in my snippet above), that last line is not underlined as an error. – Whelch Sep 24 '20 at 15:33
  • oh huh. . . I just opened the example you gave, and you're right, it's showing as an error for you. Ok clearly this is a problem on my end. Maybe I'm not using the latest version of typescript. Lemme try updating that real quick~~ – Whelch Sep 24 '20 at 15:35

1 Answers1

0

Thanks to @jcalz's tsplay link, I was able to determine that the issue stems from Typescript's default function type handling, which is bivariance. If you disable strictFunctionTypes in the tsplay link above, then it will incorrectly check the types. But with strictFunctionTypes enabled, the types flow through, as they should.

This stack overflow does a good job of explaining what that compiler option does.

Whelch
  • 2,003
  • 2
  • 11
  • 14