IDBValidKey
の値またはIDBValidKey
に変換された値を返すことを目的とした関数があります。三項演算子を使用して関数を記述した場合、正常に機能しますが、if-elseステートメントとして記述した場合、コンパイラエラーが発生します。
interface IDBValidKeyConvertible<TConverted extends IDBValidKey> {
convertToIDBValidKey: () => TConverted;
}
function isIDBValidKeyConvertible<TConvertedDBValidKey extends IDBValidKey>(object: unknown): object is IDBValidKeyConvertible<TConvertedDBValidKey> {
return typeof((object as IDBValidKeyConvertible<TConvertedDBValidKey>).convertToIDBValidKey) === "function";
}
type IDBValidKeyOrConverted<TKey> = TKey extends IDBValidKeyConvertible<infer TConvertedKey> ? TConvertedKey : TKey;
function getKeyOrConvertedKey<TKey extends IDBValidKey | IDBValidKeyConvertible<any>>(input: TKey): IDBValidKeyOrConverted<TKey> {
if (isIDBValidKeyConvertible<IDBValidKeyOrConverted<TKey>>(input)) {
return input.convertToIDBValidKey();
} else {
return input;
}
}
function getKeyOrConvertedKeyTernary<TKey extends IDBValidKey | IDBValidKeyConvertible<any>>(input: TKey): IDBValidKeyOrConverted<TKey> {
return (isIDBValidKeyConvertible<IDBValidKeyOrConverted<TKey>>(input)) ? input.convertToIDBValidKey() : input;
}
getKeyOrConvertedKeyTernary
はエラーを生成しませんが、else
のgetKeyOrConvertedKey
ブロックはこのエラーを生成します:
Type 'TKey' is not assignable to type 'IDBValidKeyOrConverted<TKey>'.
Type 'string | number | Date | ArrayBufferView | ArrayBuffer | IDBArrayKey | IDBValidKeyConvertible<any>' is not assignable to type 'IDBValidKeyOrConverted<TKey>'.
Type 'string' is not assignable to type 'IDBValidKeyOrConverted<TKey>'.
三項演算子とif-elseステートメントは同等ではありませんか?
ありがとう!
一見同じように見える3項演算子構成体が生成しないのに、 'if-else'ステートメントを使用するとTypeScriptコンパイラエラーが生成されるのはなぜですか?
TypeScriptは_if-else
_を、それぞれが独立した型を持つ複数の式を持つステートメントと見なします。 TypeScriptは、3項を、真側と偽側の和集合型を持つ式と見なします。時々、その共用体型は、コンパイラーが文句を言わないように十分に広くなります。
三項演算子とif-elseステートメントは同等ではありませんか?
結構です。
違いは、式である三項から生じます。 ここでの会話 があり、Ryan Cavanaughが3項ステートメントとif/elseステートメントの違いを説明しています。 三項式の型は、true
とfalse
の結果の結合です
特定の状況では、3項式のタイプはany
です。コンパイラが文句を言わないのはそのためです。 3項は、input
型とinput.convert()
戻り型の和集合です。コンパイル時に、input
型は_Container<any>
_を拡張します。したがって、input.convert()
の戻り値の型はany
です。 any
との共用体はany
であるため、3項の型はany
です。
簡単な解決策は、_<TKey extends IDBValidKey | IDBValidKeyConvertible<any>
_でany
をunknown
に変更することです。これにより、if-elseと3項の両方でコンパイラエラーが発生します。
これが プレイグラウンドリンク で、質問を簡単に複製したものです。 any
をunknown
に変更して、コンパイラーの応答を確認してください。
_interface Container<TValue> {
value: TValue;
}
declare function hasValue<TResult>(
object: unknown
): object is Container<TResult>;
// Change any to unknown.
const funcIfElse = <T extends Container<any>>(input: T): string => {
if (hasValue<string>(input)) {
return input.value;
}
return input;
};
// Change any to unknown.
const funcTernary = <T extends Container<any>>(input: T): string =>
hasValue<string>(input)
? input.value
: input;
_
2つのまったく別の問題が発生しています:
TypeScriptにはバグまたは制限があります[1]が、これは質問が想定するものの反対です。
三項演算子を使用して関数を記述した場合、問題なく機能します
実は良くないです。 if-elseバージョンのエラーは正しく、3値バージョンも同じエラーになるはずです。
私たちのほとんどがそうであるように、私たちはコードが正しいと仮定する傾向があるので、あなたはおそらくあなたがそうしたと仮定しました。これは2番目の問題につながります。
(今は問題の簡略化されたコードで)#2の方が見やすいと思います。でも、暇があれば詳しく説明しようと思います。
[1]これはTypeScriptの欠陥ではない可能性がありますが、私が知らないif-else
と? :
の間の微妙な非等価性により予期される動作です。 Javascriptには多くのレガシーな奇妙さがあるので、私は驚かないでしょう。 [編集:参照してください Shaun Luttinの回答 私が私のものを入力しているときと同じです。]