簡體   English   中英

typescript:根據回調返回類型驗證函數參數

[英]typescript: validate function argument based on callback return type

我正在嘗試鍵入一個函數,該函數為每個數組項運行回調並使用新屬性擴展該項:

interface Row {
    a: number
}
export function extender<
  Data extends Row[],
  T extends object,
  Props extends keyof T
  >(data: Data, extend: (row: Row) => T, newProp?: Props): (T & Row)[] {
      return data.map(row => ({
          ...row,
          ...extend(row)
      }))
}

當我打電話時,這份草稿似乎正在發揮作用

extender([{ a: 1 }, { a: 2 }], () => ({ once: 1, more: 2 }), 'once')

但是,一旦我嘗試在extend回調中使用row參數,我就會得到一個錯誤Argument of type '"once"' is not assignable to parameter of type 'undefined'.

// TS_ERROR: Argument of type '"once"' is not assignable to parameter of type 'undefined'.
extender([{ a: 1 }, { a: 2 }], (row) => ({ once: 1, more: row.a * 2 }), 'once')

我錯過了什么?

概括

在您的示例中, Tobject (實際上代表所有 non-primitives )的約束。 “任何普通對象”的類型可以表示為Record<PropertyKey, any> (它使用類型實用程序Record<Keys, Type> )。 PropertyKeystring | number | symbol string | number | symbol string | number | symbol ,所以這種類型的意思是“任何具有字符串、數字和/或符號鍵的對象,這些屬性的值可以是任何類型”。

通過簡單地替換該約束,您的代碼應該可以在沒有診斷錯誤的情況下編譯。


更多的

如果輸入數組的成員不完全Row類型(相反,它們擴展了它),則函數的當前返回類型會丟失一些類型信息,並且它不承認T可以覆蓋類型的概念在map操作中對象合並過程中的屬性a 映射類型可以幫助您保留此類類型信息,甚至可以保留諸如具有明確定義的元素數量(元組)的輸入數組的長度之類的內容。 考慮以下示例:

TS游樂場

type PlainObject = Record<PropertyKey, any>;
type Mutable<T extends PlainObject> = { -readonly [K in keyof T]: T[K] };

type ExtendedTuple<A extends readonly PlainObject[], T extends PlainObject> = {
  -readonly [Index in keyof A]: Mutable<Omit<A[Index], keyof T>> & Mutable<T>;
}

type Row = { a: number };

export function extender <
  Data extends readonly Row[],
  T extends PlainObject,
  Prop extends keyof T,
>(data: Data, extend: (row: Data[number]) => T, newProp?: Prop): ExtendedTuple<Data, T> {
  return data.map(row => ({ ...row, ...extend(row) })) as unknown as ExtendedTuple<Data, T>;
}

const extended1 = extender(
  [{ a: 1 }, { a: 2 }],
  () => ({ once: 1, more: 2 }),
  'once',
); // Record<'a' | 'more' | 'once', number>[]

const extended2 = extender(
  [{ a: 1 }, { a: 2 }],
  (row) => ({ once: 1, more: row.a * 2 }),
  'once',
); // Record<'a' | 'more' | 'once', number>[]

const [
  e2o0, // Record<'a' | 'more' | 'once', number> | undefined
  e2o1, // Record<'a' | 'more' | 'once', number> | undefined
  e2o2, // Record<'a' | 'more' | 'once', number> | undefined
  e2o3, // Record<'a' | 'more' | 'once', number> | undefined
  e2o4, // Record<'a' | 'more' | 'once', number> | undefined
  // etc...
] = extended2;


// Tuple correctness:

const extended3 = extender(
  [{ a: 1, b: 1 }, { a: 2, b: 2 }, { a: 3, b: 3 }] as [Record<'a' | 'b', number>, Record<'a' | 'b', number>, Record<'a' | 'b', number>],
  (row) => ({ a: 'hi', once: 1, more: row.a * 2 }),
  'once',
); /* [
  { a: string } & Record<'b' | 'more' | 'once', number>,
  { a: string } & Record<'b' | 'more' | 'once', number>,
  { a: string } & Record<'b' | 'more' | 'once', number>,
] */

// Note the values at the tuple indexes are not potentially undefined:
const [
  e3o0, // { a: string } & Record<'b' | 'more' | 'once', number>
  e3o1, // { a: string } & Record<'b' | 'more' | 'once', number>
  e3o2, // { a: string } & Record<'b' | 'more' | 'once', number>
  e3o3, /* This error is expected
  ~~
  Tuple type '[...]' of length '3' has no element at index '3'.(2493) */
] = extended3;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM