簡體   English   中英

打字稿:映射類型中的索引簽名

[英]Typescript: index signatures in mapped type

我怎樣才能使用類型{ 'k': number, [s: string]: any }並抽象'k'number 我想要一個類型別名T使得T<'k', number>給出所述類型。


考慮以下示例:

function f(x: { 'k': number, [s: string]: any }) {}                           // ok
type T_no_params = { 'k': number, [s: string]: any };                         // ok
type T_key_only<k extends string> = { [a in k]: number };                     // ok
type T_value_only<V> = { 'k': V, [s: string]: any};                           // ok
type T_key_and_index<k extends string, V> = { [a in k]: V, [s: string]: any };// ?
  • 直接使用{ 'k': number, [s: string]: any}作為函數f的參數類型。
  • 使用[s: string]: any type -alias 中的[s: string]: any索引部分都有效
  • type -alias 中使用k extends string也有效
  • 當將k extends string[s: string]: any in same type -alias 組合時,我得到一個解析錯誤(甚至不是語義錯誤,它甚至似乎不是有效的語法)。

這在這里似乎有效:

type HasKeyValue<K extends string, V> = { [s: string]: any } & { [S in K]: V }

但在這里,我不太明白為什么它不抱怨額外的屬性( &右側的類型不應該允許具有額外屬性的對象)。


編輯

在答案中多次提到&是交集運算符,它的行為應該類似於集合論交集。 然而,當涉及到額外屬性的處理時,情況並非如此,如以下示例所示:

function f(x: {a: number}){};
function g(y: {b: number}){};
function h(z: {a: number} & {b: number}){};

f({a: 42, b: 58});  // does not compile. {a: 42, b: 58} is not of type {a: number}
g({a: 42, b: 58});  // does not compile. {a: 42, b: 58} is not of type {b: number}
h({a: 42, b: 58});  // compiles!

在這個例子中,似乎{a: 42, b: 58}既不是{a: number}類型,也不是{b: number}類型,但它以某種方式結束在交集{a: number} & {b: number} 這不是集合論交集的工作原理。

這正是我自己的& -proposal 看起來如此可疑的原因。 如果有人能詳細說明如何將映射類型與{ [s: string]: any } “相交”可以使類型“更大”而不是使其更小,我將不勝感激。


我看過問題

但這些似乎沒有直接關系,盡管名稱相似。

type HasKeyValue<K extends string, V> = { [s: string]: any } & { [S in K]: V }是定義您所追求的類型的正確方法。 但要知道的一件事是(解釋不推薦使用的標志:keyofStringsOnly ):

keyof 類型運算符返回字符串 | 當應用於具有字符串索引簽名的類型時,數字而不是字符串。

我不知道將索引限制為string類型而不是string | number string | number 實際上允許number訪問string索引似乎是一件合理的事情,因為它符合 Javascript 的工作方式(人們總是可以對數字進行字符串化)。 另一方面,您不能安全地訪問帶有字符串值的數字索引。


&類型運算符的工作方式類似於設置理論交集 - 它總是限制可能值的集合(或保持它們不變,但從不擴展)。 在您的情況下,該類型將任何非字符串鍵作為索引排除在外。 准確地說,您將unique symbol排除在索引之外。

我認為您的困惑可能來自 Typescript 處理函數參數的方式。 使用明確定義的參數調用函數與將參數作為變量傳遞的行為不同。 在這兩種情況下,Typescript 都確保所有參數都具有正確的結構/形狀,但在后一種情況下,它另外不允許額外的道具。


說明概念的代碼:

type HasKeyValue<K extends string, V> = { [s: string]: any } & { [S in K]: V };
type WithNumber = HasKeyValue<"n", number>;
const x: WithNumber = {
  n: 1
};

type T = keyof typeof x; // string | number
x[0] = 2; // ok - number is a string-like index
const s = Symbol("s");
x[s] = "2"; // error: cannot access via symbol

interface N {
  n: number;
}

function fn(p: N) {
  return p.n;
}

const p1 = {
  n: 1
};

const p2 = {
  n: 2,
  s: "2"
};

fn(p1); // ok - exact match
fn(p2); // ok - structural matching: { n: number } present;  additional props ignored
fn({ n: 0, s: "s" }); // error: additional props not ignore when called explictily
fn({}); // error: n is missing

編輯

對象字面量 - 顯式創建某種形狀的對象,如const p: { a: number} = { a: 42 }被 Typescript 以特殊方式處理。 與常規結構推理相反,類型必須完全匹配。 老實說這是有道理的,因為這些額外的屬性 - 沒有額外的可能不安全的演員 - 無論如何都無法訪問。

[...] 但是,TypeScript 認為此代碼中可能存在錯誤。 對象文字在將它們分配給其他變量或將它們作為參數傳遞時會得到特殊處理並進行額外的屬性檢查。 如果對象字面量具有“目標類型”沒有的任何屬性,您將收到錯誤消息。 [...] 繞過這些檢查的最后一種方法(可能有點令人驚訝)是將對象分配給另一個變量。

TS手冊

解決此錯誤的另一個選擇是...將其與{ [prop: string]: any }相交。

更多代碼:

function f(x: { a: number }) {}
function g(y: { b: number }) {}
function h(z: { a: number } & { b: number }) {}

f({ a: 42, b: 58 } as { a: number }); // compiles - cast possible, but `b` inaccessible anyway
g({ a: 42 } as { b: number }); // does not compile - incorrect cast; Conversion of type '{ a: number; }' to type '{ b: number; }' may be a mistake
h({ a: 42, b: 58 }); // compiles!

const p = {
  a: 42,
  b: 58
};

f(p); // compiles - regular structural typing
g(p); // compiles - regular structural typing
h(p); // compiles - regular structural typing

const i: { a: number } = { a: 42, b: 58 }; // error: not exact match
f(i); // compiles
g(i); // error
h(i); // error

這是有關交集運算符的一種推理方式。 也許它有幫助:

type Intersection = { a: string } & { b: number }

你可以閱讀Intersection為“具有屬性的對象a類型的string屬性b的類型number ”。 這恰好也描述了這種簡單的類型:

type Simple = { a: string; b: number }

並且這兩種類型是兼容的。 對於幾乎所有用途,您都可以將其中一個替換為另一個。

我希望這可以解釋為什么HasKeyValue確實與您嘗試定義的類型相同。

至於為什么T_key_and_index不起作用,是因為第一部分[a in k]: V定義了映射類型,並且在映射類型的定義中不能有額外的屬性。 如果需要向映射類型添加額外的屬性,可以使用&創建類型交集

暫無
暫無

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

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