TypeScript:意外行为的关键

TypeScript: keyof unexpected behavior

class Target {
  prop1 = "321";
}

const target = new Target()

class Target2 {
  prop2 = "123";
}

const target2 = new Target2()

type TValue<V extends object> = {
  target: V;
  key: keyof V;
}

class Case<V extends TValue<any>[]> {
  constructor(private values: V) {}
}

const someCase = new Case([
  {
    target: target,
    key: "123"
  },
  {
    target: target2,
    key: "prop2"
  }
])

成功推断出目标类型,但 keyof 没有按预期工作。我做错了什么?

我还创建了一个playground

我之前的和你的用例很相似,但是有点不同,因为我用了一个函数,而你在处理class。


type Entity<Obj, Key> = {
  target: Obj,
  key: Key
}

type IsValid<T extends Entity<any, any>[]> =
  /**
   * Infer each element of the array
   */
  T[number] extends infer Elem
  /**
   * Check if every element of the array extends Entity
   */
  ? Elem extends Entity<any, any>
  /**
   * Check if keyof Elem['object'] extends `key` property
   * 1) if [key] property is one of object properties - return true
   * 2) if at least one element does not meet your requirements return false | true,
   * because some element are ok
   */
  ? keyof Elem['target'] extends Elem['key']
  ? true
  : false
  : false
  : false;

// credits goes to 
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
  k: infer I
) => void
  ? I
  : never;

// credits https://whosebug.com/users/125734/titian-cernicova-dragomir
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;

type Validator<T extends boolean> =
  /**
   * If IsValid returns false | true (boolean) it means Error
   * otherwise - ok
   */
  IsUnion<T> extends true ?
  ['Dear developer, please do smth right']
  /**
   * I'm using empty array here, because 
   * (...flag:[])=>any evaluates to function without arguments
   */
  : []

class Case<
  Value extends { [prop: string]: any },
  Key extends keyof Value,
  Data extends Entity<Value, Key>[],
  > {
  constructor(private values: [...Data], ...flag: [...Validator<IsValid<[...Data]>>]) { }
}


class Target {
  prop1 = "321";
}

const target = new Target()

class Target2 {
  prop2 = "123";
}

const target2 = new Target2()

const result = new Case([{
  target: target, key: 'prop1'
},
{
  target: target2, key: 'prop2'
}]) // ok

const result_ = new Case([{
  target: target, key: 'prop1'
},
{
  target: target2, key: 'prop1' // wrong
}]) // error

您可以将 ['Dear developer, please do smth right'] 替换为 [never]

Here 您可以找到有关推断函数参数的更多信息