TS 面接での優れた質問 (3 つのレベルを含む) を共有して、どのレベルに回答できるかを確認してください。

青灯夜游
リリース: 2023-01-14 19:36:19
転載
2735 人が閲覧しました

TS 面接での優れた質問 (3 つのレベルを含む) を共有して、どのレベルに回答できるかを確認してください。

最近、TS の面接で良い質問を見つけたので、それを共有したいと思います。

この質問には 3 つのレベルがあります。1 つずつ見ていきましょう。

最初のレベルの要件は次のとおりです。

2 つの配列の要素を順番にマージする zip 関数を実装します (入力 [1,2,3] など)。 [4,5,6]、return [[1,4], [2,5],[3,6]]

このレイヤーは、マージ後に毎回 2 つの配列からフェッチされます。要素を取得し、それを配列に入れてから次の要素の処理を続け、配列が空になるまでこのプロセスを再帰的に続けます。

function zip(target, source) {
  if (!target.length || !source.length) return [];

  const [one, ...rest1] = target;
  const [other, ...rest2] = source;

  return [[one, other], ...zip(rest1, rest2)];
}
ログイン後にコピー

結果は正しいです:

最初のレベルは比較的単純です。次に、2 番目のレベルの要件を見てみましょう:

この zip 関数の ts 型を定義します (2 つの記述方法)

関数定義には 2 つの形式があります:

関数を通じて関数を直接宣言します:

function func() {}
ログイン後にコピー

そして匿名関数を宣言して変数に代入します:

const func = () => {}
ログイン後にコピー

パラメータの型と戻り値は両方とも配列ですが、具体的な型は不明なので、unknown[] と書くことができます。 。

したがって、2 つの関数タイプの定義は次のようになります。

は、関数の直接関数宣言でもあります。型とインターフェイスの宣言 その後、関数の型が変数の型に双方向で追加されます。

特定の要素タイプが不明なため、unknown が使用されます。

ここで any と未知の違いを尋ねることができます:

any と未知の両方は任意の型を受け取ることができます:

ただし、any も受け取ることができますまた、任意のタイプに割り当てられますが、不明です。

これは他のタイプを受信するためにのみ使用されるため、unknown がどのタイプよりも適切で安全です。

このレベルも比較的基本的な ts 構文であり、3 番目のレベルはさらに難しくなります:

型プログラミングを使用して、[1, 2 でパラメーターを渡すなど、正確な型ヒントを実現します。] ,3]、[4,5,6] の場合、戻り値の型は [[1,4], [2,5],[3,6]]

としてプロンプト表示されます。 #here 戻り値の型を正確にする必要がある場合は、パラメータの型に基づいて戻り値の型を動的に生成する必要があります。

以上です:

2 つの型パラメーター Target と Source を宣言します。制約は、任意の要素型の配列型である、unknown[] です。

これらの 2 つの型パラメーターは、渡される 2 つのパラメーターの型です。

戻り値はZipによって計算されます。

次に、高度な型の Zip を実装する必要があります。

渡される型パラメーターは 2 つの配列型であり、そこから各要素を抽出して結合する必要もあります。

パターン マッチングを使用して要素を抽出できます:

したがって、この型は次のように定義できます:

type Zip<One extends unknown[], Other extends unknown[]> =
    One extends [infer OneFirst,...infer Rest1]
      ? Other extends [infer OtherFirst, ...infer Rest2]
        ? [[OneFirst, OtherFirst], ...Zip<Rest1, Rest2>]
        : []
      : [];
ログイン後にコピー

2 つの配列の最初の要素をそれぞれ抽出し、新しい配列を構築します。次に、配列が空になるまで、残りの配列に対してこれを再帰的に実行します。

これにより、必要な高度な型が実現されます:

#ただし、これを戻り値として関数に追加すると、エラーが報告されます:

関数を宣言するときにパラメーターが何であるかがわからないため、当然、Zip の値を計算することはできません。そのため、型が存在します。ここが不一致です:

それではどうすればよいでしょうか?

は関数のオーバーロードで解決できます:

#ts は関数のオーバーロードをサポートしており、複数の型の関数の型定義を同じ名前で書くことができます。最後に関数の実装を記述します。これにより、この関数が使用されるときに関数の型がパラメーターの型に応じて一致するようになります。

型プログラミングを使用する関数は、この方法で記述された場合、エラーを報告しません。

見てみましょう:

戻り値の型が間違っているのはなぜですか?

#実際、現時点では、一致する関数の型は正しいですが、推定された型はリテラル型ではありません。

現時点では const として追加できます。

ただし、const として追加すると、読み取り専用 [1,2,3]

この型は一致しません。したがって、type パラメータの宣言に readonly:

を追加する必要がありますが、Zip 関数の型が再び一致しません。

この型が使用されるすべての場所に readonly を追加する必要がありますか?

必要ありません。読み取り専用の変更を削除してもいいですか?

Typescript には高度な読み取り専用タイプが組み込まれています:

インデックス タイプの各インデックスに読み取り専用の変更を追加できます:

しかし、読み取り専用の変更を削除する高度な型はありません。これは自分で実装できます:

マッピング型の構文を使用して、新しいインデックス タイプ。-readonly を追加すると、読み取り専用の変更が削除されます。

#学生の中には、配列型はインデックス型でもあるのかと疑問に思う人もいるかもしれません。

はい、インデックス型は複数の要素を集約する型ですので、オブジェクトも配列もクラスもすべてそうです。

したがって、自然に配列で使用できます:

(正確には、これはタプルと呼ばれます。タプルには固定数の要素があります。配列)

次に、Zip を渡す前に Mutable を使用して readonly を削除するだけです:

もう一度試してみましょう:

## #######終わり!これで戻り値の型が正しくなりました。

しかし、まだ問題があります。リテラルが直接渡されない場合、リテラルの型を推定することはできません。現時点では、何かが間違っているようです:

しかし、私たちは皆、オーバーロードされた型を宣言しているのではないでしょうか?

リテラル型を推定できない場合は、これと一致する必要があります:

しかし実際には、最初のものと一致します:

現時点では、次の 2 つの関数タイプの順序を変更するだけで済みます。

##今回は、リテラルパラメータの状況は依然として正しいです:

なぜですか?

オーバーロードされた関数の型は上から下まで一致するため、どれか一つでも一致すれば適用されます。

非リテラル値の場合、型はnumber[]であり、unknown[]の型と一致するため、その関数型が有効になります。

リテラルの場合、導出は読み取り専用 [1,2,3] ですが、読み取り専用があるため、unknown[] には一致せず、引き続き一致します。型パラメータを持つ関数の型が一致しました。

このようにして、両方の場合に適切な関数タイプが適用されます。

コード全体は次のようになります:

type Zip<One extends unknown[], Other extends unknown[]> = One extends [
  infer OneFirst,
  ...infer Rest1
]
  ? Other extends [infer OtherFirst, ...infer Rest2]
    ? [[OneFirst, OtherFirst], ...Zip<Rest1, Rest2>]
    : []
  : [];

type Mutable<Obj> = {
  -readonly [Key in keyof Obj]: Obj[Key];
};

function zip(target: unknown[], source: unknown[]): unknown[];

function zip<Target extends readonly unknown[], Source extends readonly unknown[]>(
  target: Target,
  source: Source
): Zip<Mutable<Target>, Mutable<Source>>;

function zip(target: unknown[], source: unknown[]) {
  if (!target.length || !source.length) return [];

  const [one, ...rest1] = target;
  const [other, ...rest2] = source;

  return [[one, other], ...zip(rest1, rest2)];
}

const result = zip([1, 2, 3] as const, [4, 5, 6] as const);

const arr1 = [1, 2, 3];
const arr2 = [4, &#39;5&#39;, 6];

const result2 = zip(arr1, arr2);
ログイン後にコピー

ts playground 地址

总结

今天我们做了一道综合的 ts 面试题,一共有三层:

第一层实现 js 的逻辑,用递归或者循环都能实现。

第二层给函数加上类型,用 function 声明类型和 interface 声明函数类型两种方式,参数和返回值都是 unknown[]。

第三层是用类型编程实现精准的类型提示,这一层需要拿到参数的类型,通过提取元素的类型并构造出新的数组类型返回。还要通过函数重载的方式来声明类型,并且要注意重载类型的声明顺序。

as const 能够让字面量推导出字面量类型,但会带有 readonly 修饰,可以自己写映射类型来去掉这个修饰。

其实这也是我们学习 ts 的顺序,我们先要能把 js 逻辑写出来,然后知道怎么给函数、class 等加 ts 类型,之后学习类型编程,知道怎么动态生成类型。

其中类型编程是 ts 最难的部分,也是最强大的部分。攻克了这一层,ts 就可以说学的差不多了。

【相关推荐:javascript学习教程

以上がTS 面接での優れた質問 (3 つのレベルを含む) を共有して、どのレベルに回答できるかを確認してください。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート