angulartypescriptstrong-typing

How to infer the right types of component's @Input


I'm looking for a way to infer the right types of a component's @Inputs. So far, I'm only able to target the @Input properties using the following:

// Method in a service
setComponentInputs<T> (props: { [key in keyof T]?: any }) {
   console.log(props);
}

But as you can see the type is still any
Also, the current approach allows to target everything within the Component, including methods (e.g OnInit), but I'm not so sure if there's is a trick to target only the @Inputs.

Let me provide you with more context, this is a standard Angular Component:

@Component({
  selector: '',
  templateUrl: './foo.component.html',
  styleUrls: ['./foo.component.scss']
})
export class FooComponent implements OnInit {
  @Input() myProp: string;
  @Input() otherProp: CustomProp;

  constructor() {}

  ngOnInit() {
  }
}

The idea is to call setComponentInputs(props) while restricting the props argument to match the component @Inputs properties

// this one should compile
myService.setComponentInputs<FooComponent>({myProp: 'Hello there!'});

// this one should raise an error (because the type of myProp is string)
myService.setComponentInputs<FooComponent>({myProp: 123});

// this one should raise an error (because the referenced property don't even exist)
myService.setComponentInputs<FooComponent>({newProp: 'Hello!'});

Edit 1: I've found a way to overcome the main issue using Mapped Types

// Method in a service
setComponentInputs<T> (props: { [P in keyof T]?: T[P] }) {
   console.log(props);
}

But then @andrei pointed out a different and easier approach:

// Method in a service
setComponentInputs<T> (props: Partial<T>) {
   console.log(props);
}

The remaining piece will be how to allow only the properties while excluding Functions (e.g OnInit)

// this one should raise an error (because ngOnInit is a Function)
myService.setComponentInputs<FooComponent>({ngOnInit: () => {});

Solution

  • Here's the solution based on Andrei's feedback.

    // Method in a service
    setComponentInputs<T> (props: { [P in keyof T]?: T[P] extends Function ? never : T[P] }) {
       console.log(props);
    }
    

    Next step would be to create a generic type and use it where needed. Thanks @andrei :)