Quadear
Quadear

Reputation: 517

Typescript: Type inference for array Elements

I would like to obtain the following behaviour without using a T as any:

interface Test<T> {
  sameField: string;
  test: T
  testFunction: (e: T) => void
}

const array: Array<Test</*something something*/_>> = [
  {
    sameField: "foo",
    test: 1,
    testFunction: (e) => {
      e.toExponential(2) // Should be Valid
      e.indexOf('e') // Should be Invalid
    }
  },
  {
    sameField: "bar",
    test: "baz",
    testFunction: (e) => {
      e.indexOf('e') // Is Valid
      e.toExponential(2) // Should be Invalid
    }
  }
]

The idea behind it is just a way of telling Typescript "let the array elements handle their typing themselves". Is it only possible to do in Typescript ?

Upvotes: 0

Views: 84

Answers (1)

TS is unable to infer e argument in such kind of data structure on his own.

You have several options here.

First

Define your test property upfront:

type Main = {
    foo: string;
    bar: number;
};

type Validator<T extends keyof Main> = {
    test: T;
    isValid: (value: Main[T]) => boolean;
};

type Values<T> = T[keyof T]

type MainValidationRule = Values<{
    [P in keyof Main]: {
        test: P;
        isValid: (value: Main[P]) => boolean;
    }
}>

const validators: Array<MainValidationRule> = [
    { test: 'foo', isValid: (value/* infered to stirng */) => true },
    { test: 'bar', isValid: (value/* infered to number */) => true }
]

Second

interface Test<T> {
    field: string;
    test: T
    fn: (e: T) => void
}

const builder = <Test, Field>(test: Test, field: Field, fn: (e: Test) => any) => ({
    field,
    test,
    fn
})

const array = [builder(2, 'hello', (e/**infered to number */) => 42)]

Here, in my blog, you can find more information about using and typing callbacks

There is one more way to do it:

interface Foo {
    sameField: "foo",
    test: 1,
    testFunction(e: this['test']): void
}

const x: Foo = {
    sameField: "foo",
    test: 1,
    testFunction(e) {
        e.toExponential(2) // ok
        e.indexOf('e') // error
    }
}

But as you might have noticed, you should declare your sameField and test upfront

There are more options to do it, but I believe it will be an overhead

Upvotes: 1

Related Questions