0
interface IYears {
  one: string;
  two: string;
  three: string;
}

function transformYears(years: Array<keyof IYears>): [if all items of "years" includes in keyof IYears] ? IYears : Partial<IYears> {
  return years.reduce((acc, year) => ({
    ...acc,
    [year]: 'foo'
  }), {})
}

const yearsFirst = transformYears(['one', 'two']) // type of yearsFirst is Partial<IYears>

const yearsSecond = transformYears(['one', 'two', 'three']) // type of yearsFirst is IYears

How modified transformYears to match type of yearsFirst and yearsSecond? And possible check condition "if all items of "years" includes in keyof IYears" in ts?

1 Answers1

0

You need to make the function generic to get this to work.

function transformYears<
  Y extends (keyof IYears)[]
>(years: [...Y]): Exclude<keyof IYears, Y[number]> extends never 
  ? IYears 
  : Partial<IYears> 
{
  return years.reduce((acc, year) => ({
    ...acc,
    [year]: 'foo'
  }), {}) as any
}

If we store the passed array in the generic type Y, we can use Exclude to compute the difference between keyof IYears and Y[number]. If Y[number] contains all keys of IYears, this evaluates to never which we can check for in the condition.

Playground


After thinking about it, we can make it even a bit shorter:

function transformYears<
  Y extends (keyof IYears)[]
>(years: [...Y]): keyof IYears extends Y[number] ? IYears : Partial<IYears> {
  return years.reduce((acc, year) => ({
    ...acc,
    [year]: 'foo'
  }), {}) as any
}

Playground

Tobias S.
  • 21,159
  • 4
  • 27
  • 45
  • Thank for cool ideas! Based on your answer make [improvements](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgJIE0JygZ2QbwChkTkB7ECALmRzClAHMBuY0sAdzJroZBbYkwACygRqtek1YBfQoRgBXEAjDAKyenBA4YZKAFtM2HAB5ByACrIIAD0ggAJngxZcAGgvob9iE7wAglBQcACepgDWEKFkMFYAfJ6kyACyPg7OyFExcdZ2GXjoANogigYARtAAusgA-FbINAAK2GpwADamlvGE8QAUoW44NEUAdOPoVQCUNGlEyWJgilAgyIMmo2KOikh9Fsl9iAjua25TyAC88ch988n3JOOjR0kP90XrUFU0AOR6ZD99qQZFNXg98DJkHA8CkLFNZPIEBQ6KcTAAxYC4MCXTQhHR6QzGXB9Io-CgQH4nH6cAHTZjIAD0DM0oQADihYqjcBischgHgWlA2p1XCYeoQkTpsZ8cABlCCSxw4rT4-RGIYksmUSnIalcHXU0TiH50xnMsBsjlxGU8lH8tBEnBAA) – RocketArtyom Jun 29 '22 at 22:01
  • Мove types to the header is good idea? Also do not understand how work construction ```keyof IYears extends Y[number]```, especially what mean ```Y[number]``` for ts, like instead of ```number``` put index 0, 1, 2,...length of ```years``` and result compared with keyof IYears, where to look to find out more? – RocketArtyom Jun 29 '22 at 22:34
  • `Y` is a generic type which holds the array you pass as the argument. We can now call `Y[number]` to get a union of all the elements inside of `Y` (see [this answer](https://stackoverflow.com/questions/45251664/typescript-derive-union-type-from-tuple-array-values)). When we use `keyof IYears extends Y[number]` we essentially check if every key in `IYears` is also in the union of the elements inside the array. If there are more strings in `keyof IYears` than in `Y[number]` the first one would not extend the second one anymore. – Tobias S. Jun 29 '22 at 23:04
  • Thanks for help! I started to understand the syntax better – RocketArtyom Jun 30 '22 at 07:19