Home > Mobile >  Typescript strange behavior
Typescript strange behavior

Time:07-11

TS Playground

function foo(a: number, b: number) {
  return a   b;
}

type Foo1 = typeof foo extends (...args: unknown[]) => unknown ? true : false; // false
type Foo2 = typeof foo extends (...args: any[]) => unknown ? true : false; // true

Why does it works with any[], but does not with unknown[]?

It works with tuples and ReturnTypes, but not with rest parameters.

type Foo = ['bar', 'baz'] extends unknown[] ? true : false; // true

CodePudding user response:

Edit: Read comment on original post, the following is a mistaken answer based on incorrect knowledge I had of Typescript.


This is because of the type restrictions on using a value assigned to a unknown type. For example, variables of the number type can be assigned values from variables of the any type, but cannot be assigned values from variables of the unknown type.

E.g.

let value:any = 5;
let value2:unknown = 6;

let b:number = value;
let c:number = value2;

Will let b compile, but not c, giving an error "Type 'unknown' is not assignable to type 'number'."

CodePudding user response:

Function comparison in TS is, perhaps, unexpectedly complicated (or at least unintuitive), and the handbook has a section specifically about this.

In the example code you've shown, you've run into the issue of function parameter bivariance. To work around this: instead of comparing the entire function, you can compare its parameters and return type in isolation to those of the other function in question. Consider the following:

In a comment to your question, Tobias S. mentioned another answer which explains different types of variance in more detail.

TS Playground

type Fn<Params extends readonly any[] = readonly any[], Result = any> =
  (...params: Params) => Result;

type FnAExtendsFnB<FnA extends Fn, FnB extends Fn> =
  Parameters<FnA> extends Parameters<FnB>
    ? ReturnType<FnA> extends ReturnType<FnB>
      ? true
      : false
    : false;

type AExtendsB<A, B> = A extends B ? true : false;

// The function from your question:
type Foo = Fn<[a: number, b: number], number>;

type UnknownParamsUnknownResult = Fn<unknown[], unknown>;
type AnyParamsUnknownResult = Fn<any[], unknown>;

declare const T1: AExtendsB<Foo, UnknownParamsUnknownResult>; // false

declare const T2: AExtendsB<Foo, AnyParamsUnknownResult>; // true

declare const T3: FnAExtendsFnB<Foo, UnknownParamsUnknownResult>; // true

declare const T4: FnAExtendsFnB<Foo, UnknownParamsUnknownResult>; // true

  • Related