[解決済み] TypeScriptでObject.keysがkeyof型を返さないのはなぜですか?
質問
タイトルがすべてを物語っている - なぜそうしないのか
Object.keys(x)
という型を TypeScript で返せるのはなぜか?
Array<keyof typeof x>
? それは
Object.keys
がそうであるように、TypeScript の定義ファイルの作成者が戻り値の型を単純に
keyof T
.
彼らの GitHub リポにバグを記録するべきか、それとも彼らのために修正する PR を送るべきか?
どのように解決するのですか?
現在の戻り値型(
string[]
) は意図的なものです。なぜでしょうか?
このような何らかの型を考えてみましょう。
interface Point {
x: number;
y: number;
}
このようなコードを書くのです。
function fn(k: keyof Point) {
if (k === "x") {
console.log("X axis");
} else if (k === "y") {
console.log("Y axis");
} else {
throw new Error("This is impossible");
}
}
質問してみましょう。
よく型付けされたプログラムにおいて、合法的に
fn
をエラーケースにヒットさせることができますか?
は
欲しい
答えはもちろん、「いいえ」です。しかし、このことが
Object.keys
?
では、次のように考えてみましょう。 その他 のコードを考えてみましょう。
interface NamedPoint extends Point {
name: string;
}
const origin: NamedPoint = { name: "origin", x: 0, y: 0 };
TypeScriptの型システムに従って、すべての
NamedPoint
は有効な
Point
s.
では、次のように書いてみましょう。 をもう少し書いてみましょう。 :
function doSomething(pt: Point) {
for (const k of Object.keys(pt)) {
// A valid call iff Object.keys(pt) returns (keyof Point)[]
fn(k);
}
}
// Throws an exception
doSomething(origin);
よく型付けされたプログラムが例外を投げました!
ここで何かが間違っていたのです!
を返すことで
keyof T
から
Object.keys
という仮定を破っていることになります。
keyof T
は網羅的なリストを形成するという仮定に違反しています。なぜなら、オブジェクトへの参照を持つことは
型の参照を持つことは
のスーパータイプでないことを意味するからです。
のスーパータイプではありません。
.
基本的に、以下の4つのうち(少なくとも)1つは真であるはずがない。
-
keyof T
のキーの網羅的なリストです。T
- 追加のプロパティを持つ型は、常にその基本型のサブタイプである
- スーパータイプの参照によってサブタイプの値を別名にすることは合法です。
-
Object.keys
は返すkeyof T
ポイント1を捨てると
keyof
はほとんど無意味です。
keyof Point
ではない何らかの値である可能性があるからです。
"x"
または
"y"
.
ポイント2を捨てると、TypeScriptの型システムが完全に破壊されます。オプションではありません。
3を捨てると、TypeScriptの型システムが完全に破壊されます。
4は問題なく、プログラマは自分が扱っているオブジェクトが、自分が持っていると思っているオブジェクトのサブタイプの別名である可能性があるかどうかを考えることになる。
これを実現するための"missing feature"は
合法でありながら矛盾しない
は
正確なタイプ
を宣言することで、新しい
種類
を宣言することができます。この機能があれば、おそらくは
Object.keys
を返す
keyof T
に対してのみ
T
として宣言された
正確には
.
追記:確かにジェネリックですが?
コメンテーターは次のようにほのめかしています。
Object.keys
は安全に
keyof T
を安全に返すことができます。これはまだ間違っています。考えてみてください。
class Holder<T> {
value: T;
constructor(arg: T) {
this.value = arg;
}
getKeys(): (keyof T)[] {
// Proposed: This should be OK
return Object.keys(this.value);
}
}
const MyPoint = { name: "origin", x: 0, y: 0 };
const h = new Holder<{ x: number, y: number }>(MyPoint);
// Value 'name' inhabits variable of type 'x' | 'y'
const v: "x" | "y" = (h.getKeys())[0];
またはこの例では、明示的な型引数さえ必要ありません。
function getKey<T>(x: T, y: T): keyof T {
// Proposed: This should be OK
return Object.keys(x)[0];
}
const obj1 = { name: "", x: 0, y: 0 };
const obj2 = { x: 0, y: 0 };
// Value "name" inhabits variable with type "x" | "y"
const s: "x" | "y" = getKey(obj1, obj2);
関連
-
[解決済み] Typescript オブジェクトのインデックス付きメンバの型を強制する?
-
[解決済み] TypeScriptのオブジェクトリテラルでの型定義
-
[解決済み] Typescript によるインターフェース型チェック
-
[解決済み] TypeScriptのクラス型チェック
-
[解決済み] Typescript Date Type?
-
[解決済み】TypeScriptで`keyof`に似た`valueof`はありますか?
-
[解決済み] TypeScriptで Object.keys return string[].
-
[解決済み] ts ES5/ES3の非同期関数やメソッドには、「Promise」コンストラクタが必要です。
-
[解決済み] ユニオン型からインターセクション型への変換
-
[解決済み] tsc が `TS2307: Cannot find module` for a local file をスローします。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] タイプスクリプトです。タイプ '{ "A": string; }' で 'string' 型のパラメータを持つインデックス署名が見つかりませんでした。
-
[解決済み] TypeScriptの予約語 "type "とは何ですか?
-
[解決済み] ts ES5/ES3の非同期関数やメソッドには、「Promise」コンストラクタが必要です。
-
[解決済み] Typescriptでインターフェースやクラスを使用する場合 [重複].
-
[解決済み] 文字列ユニオンから文字列配列へ
-
[解決済み] tsconfig.jsonのtargetは何のためにあるのですか?
-
[解決済み] Typescript のプリミティブ型:"number" と "Number" の違い(TSC は大文字と小文字を区別しない)?
-
[解決済み] Visual Studio Code - インポート引用符の設定を調整する
-
[解決済み] グローバルスコープの拡張は、外部モジュールまたはアンビエントモジュール宣言にのみ直接ネストすることができます(2669)
-
[解決済み] モジュールと名前空間 - Import vs Require Typescript