0
type Types = 'edit' | 'add' | 'delete';
type A = { a: number };
type B = {
    b: string
};
type C = {
    b: boolean
}
type TValues = A | B | C;

function test(type:'add', values: A[]): void;
function test(type: 'edit' | 'delete', values: B[] | C[]): void;
function test(type:Types, values:TValues[]) {
    if(type === 'add') {
         console.log(values.a); // Property 'a' does not exist on type 'TValues[]'.
         return;
    };

    if(type === 'edit') {
         console.log(values.b); // Property 'b' does not exist on type 'TValues[]'.
         return;
    }
    if(type === 'delete') {
        console.log(values.b) // Property 'b' does not exist on type 'TValues[]'.
        return;
    }
}

test('add', [ { a: 1 } ]);
test('edit', [ { b: 'b' } ]);
test('delete', [ {b: true} ]);

I get the error - Property [x] does not exist on type TValues.

Required functionality is if type === 'edit' or 'delete' type of "values" should be of type B[] or C[]. If it's add, then its type should be A[].

Here is a link to the playground - link

3 Answers3

0

You should write a method with two signatures. You don't need generics here.

type TAdd = {a:number};
type TEdit = {
    a: string;
    b: string
};

function a(type: 'add', values: TAdd): void;
function a(type: 'edit', values: Edit): void;
function a(type: string, values: any) {
    if(type === 'add') {
        values = values as TAdd;
        console.log(values.a);
    };
    if(type === 'edit') {
        values = values as TEdit;
        console.log(values.a, values.b);
    }
}

a('add', { a:1 });
a('edit',{ a: 'a',b: 'a' });

You have to add the extra type assertion (I don't know) to get autocomplete inside the function. This works as expected. TypeScript playground

blaumeise20
  • 2,148
  • 8
  • 26
0

It's easier if you make the 'type' discriminator part of your runtime value and make that explicit in your type. TypeScript will then be able to infer the type within value assertions:

interface Foo {
    type: "foo";
    value: number;
}

interface Bar {
    type: "bar";
    value: string;
}

const f = (v: Foo | Bar) => {
    if (v.type === "foo") {
        // v.value is number
    } else {
        // v.value is string
    }
}

Gerard van Helden
  • 1,601
  • 10
  • 13
0

I think you're looking for a discriminated union.

type TAdd = {
    type: 'add';
    a:number;
};
type TEdit = {
    type: 'edit';
    a: string;
    b: string;
};

function a(value: TAdd | TEdit) {
    if(value.type === 'add') {
         console.log(value.a);
    };
    if(value.type === 'edit') {
        console.log(value.a, value.b);
    }
}

a({ type: 'add', a:1 }); // Valid 
a({ type: 'edit', a: 'a', b: 'a' }); // Valid 
a({ type: 'add', a: 'a', b: 'a' }); // Error 

• There is a similar Stack overflow question here too

Playground link for the code snippet above

Parker Tailor
  • 1,290
  • 13
  • 12