Crocsx
Crocsx

Reputation: 7589

Change Output type based on Input value

I have a selector component.

The selector take a value that can be either a string/number etc... or an array of string/number etc..

https://stackblitz.com/edit/angular-ivy-gjbpvw?file=src/app/app.component.html

export class SelectComponent<T> implements OnInit {
  @Input() value: T | T[]
  @Input() multiple: boolean;

  @Output() valueChange = new EventEmitter<T | T[]>() 
  constructor() { }

  ngOnInit() {
  }

}

I would like to use all strict check types, but I face a problem with this input.

<app-select [(value)]="test"></app-select>
<app-select [multiple]="true" [(value)]="test2"></app-select>

export class AppComponent  {
  test = "";
  test2: string[] = []
}

The above will complain about

type 'string | string[]' is not assignable to type 'string'.

I wonder if there is a way without remove the strict check and without making 2 components, to change the Output value based on multiple

If multiple = true output will always be an array If multiple = false output will never be an array

Is there a way ?

Upvotes: 1

Views: 865

Answers (2)

Ken La
Ken La

Reputation: 170

I tried one thing and it seems to work :

select.component.ts

import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';

@Component({
  selector: 'app-select',
  templateUrl: './select.component.html',
  styleUrls: ['./select.component.css']
})
export class SelectComponent<T, U extends boolean, V = U extends true ? T extends any[] ? T : T[] : T extends (infer Tt)[] ? Tt : T> implements OnInit {
  @Input() value: V;
  @Input() multiple: U;

  @Output() valueChange = new EventEmitter<V>();

  constructor() {}

  ngOnInit() {}
}

So I had the type U and V in your component. U is only a boolean and type the input multiple.

V check if U is true. If yes check if T is an array as it should be. If it's the case, V is equal to T else V is equal to T[]. If U is false, check if T is an array. If true so get the type of element without the array and assign it to V, else V = T.

Upvotes: 2

Aviad P.
Aviad P.

Reputation: 32629

The Angular complaint is justified :) - but you should realize that it is complaining about the output not the input to the component.

It is complaining that the EventEmitter<T | T[]> can emit a type that is incompatible with the type of the input variable.

If you change the declaration of test and test2 to the following, it will work:

test: string | string[] = "";
test2: string | string[] = [];

Upvotes: 1

Related Questions