0

I want to have a generic function that takes chocolate on any object it gets called with. No other property matters. How would you do it?

const sweets = {
      candy: 'Skittles',
      chocolate: 'Milka',
      lollipop: 'ChupaChups',
    }
    
const getChocolate = ({chocolate}: {chocolate: string}) => chocolate.includes('something');
TheGabornator
  • 523
  • 1
  • 7
  • 20
  • 1
    I'm not understanding the question; can you explain how the code example relates to your question, or what "this one in particular" refers to? The title of the question sounds like [this one](https://stackoverflow.com/questions/48230773/how-to-create-a-partial-like-that-requires-a-single-property-to-be-set/48244432) but reading the text of the question has left me confused. – jcalz Jan 22 '21 at 19:38
  • I'm sorry for that. I just changed it, I hope it makes more sense now. – TheGabornator Jan 22 '21 at 19:50
  • 1
    Can you explain what's wrong with your implementation of `getChocolate` then? You can call `getChocolate(sweets)` and it works fine. Do you need to do it on some object literal and want to avoid excess property checking? I feel like there should be more words and/or more example code showing what you do and do not want to accept. – jcalz Jan 22 '21 at 19:52
  • It's stupid, but I think I solved my problem with the question. My function takes the whole object as argument and accesses `myObj.chocolate.includes('something')`. The compiler was complaining that it wasn't always invoked with the same types. But if I do exactly like in my example, the compiler won't complain. – TheGabornator Jan 22 '21 at 20:01

1 Answers1

1

Use a type or an interface, then declare optional properties (using ?).

Using a type:

type Sweets = {
  candy?: string;
  chocolate: string;
  lollipop?: string;
}

Using an interface:

interface Sweets {
  candy?: string;
  chocolate: string;
  lollipop?: string;
}

And then in your script:

const sweets: Sweets = {
  candy: 'Skittles',
  chocolate: 'Milka',
  lollipop: 'ChupaChups',
};

const getChocolate = ({ chocolate }: Sweets): boolean => chocolate.includes('something');

Update to define type on function instead of on argument

You can use a generic type on your function to dynamically-type the function's argument. I believe something like this should do what you want:

/*

'T' is a generic type, you can set the type
when you _call_ the function,
not when you define it
extending Record<'chocolate', string> means that
although we don't know the argument type yet,
we know it should at least have a chocolate: string property

*/
const getChocolate = <T extends Record<'chocolate', string>>(
  { chocolate }: T,
): boolean => chocolate.includes('something');

// the type for arg `{ chocolate }` will be set when the function is _called_

// argument for `{ chocolate }` is now of type 'Sweets'
getChocolate<Sweets>(mySweets);
bmdev
  • 386
  • 1
  • 7
  • Thanks a lot! Option 2 is great. I didn't make my point clear though. I want to have a generic function that takes chocolate on any object it gets called with. So it just has to have that property. – TheGabornator Jan 22 '21 at 19:49
  • `[k: string]?: String;` is not valid TypeScript. Index signatures cannot be optional. (furthermore you almost always want `string` and not `String`) – jcalz Jan 22 '21 at 19:50
  • ah whoops you're right, ty – bmdev Jan 22 '21 at 20:14
  • @TheGabornator I've updated my answer at the bottom with jcalz feedback and also to better-answer your question at the bottom – bmdev Jan 22 '21 at 20:26