Adan Wattad
Adan Wattad

Reputation: 11

How to check if a union type parameter (T | [string, T][]) is of type T and not [string, T][]?

I want to write a function that gets a parameter of type T | [string, T][] and checks if the parameter is of type T and not of type [string, T][]. example:

const example: null | [string, null][] = [['addr1', null], ['addr2', null] ] 
it's type is not null (null is T)

T is not class, it is usually string, number, string[]....

My first implementation was:

function isTypeT<T>(response: T | [string,T][], singleResType: new () => T): boolean {
  return response instanceof singleResType;
}

but it requires that the type T must have a constructor.
(typeof function will not help because :

typeof string[] == typeof [string, string[]][] == object

)

I also thought about this:

function isTypeT<T>(response: T | [string, T][], singleRes: T): boolean {
    if (Array.isArray(response) && Array.isArray(singleRes)) {
        return isTypeT(response[0], singleRes[0]);
    } 
     else if (typeof response !== typeof singleRes || Array.isArray(response) || Array.isArray(singleRes)) {
        return false;
    }
    return true;
}

but passing a sample object every time to check the function doesn't seem like a good typescript implementation.

So, I would appreciate it if anyone could suggest another way to check the types.

Upvotes: 1

Views: 217

Answers (2)

Bergi
Bergi

Reputation: 665145

This is not possible in general. Consider

type Infinite = [string, Infinite][]
isTypeT<Infinite>([])

where you cannot distinguish T (Infinite) from [string, T][] since they are the same. Sure, if T is not an array type, you can distinguish them, but you cannot do that for an arbitrary (parameterised) generic type.

Upvotes: 2

Dimava
Dimava

Reputation: 10899

import { type } from 'arktype'

// not gonna write the checks myself
const stringAnyTupleArray = type([['string', 'any'], '[]']);

const isStringAnyTupleArray = stringAnyTupleArray.allows;
// const isStringAnyTupleArray: (data: unknown) => data is [string, any][]

function isNot<T>(isT: (v: unknown) => v is T) {
  return function isNotT<V>(v: V): v is Exclude<V, T> {
    return !isT(v);
  }
}

const isNotStringAnyTupleArray = isNot(isStringAnyTupleArray);
// const isNotStringAnyTupleArray: <V>(v: V) => v is Exclude<V, [string, any][]>

function foo<T>(t: T | [string, T][]) {
  if (isNotStringAnyTupleArray(t)) {
    ;; t
    // ^?
  } else {
    ;; t // doesn't work
    // ^?
  }
  if (isStringAnyTupleArray(t)) {
    ;; t
    // ^?
  }
}

Upvotes: 0

Related Questions