Ich bin vor Kurzem auf eine gute TS-Interviewfrage gestoßen und möchte sie gerne teilen.
Diese Frage hat 3 Ebenen, schauen wir sie uns einzeln an.
Die Anforderungen der ersten Ebene lauten wie folgt:
Implementieren Sie eine Zip-Funktion, um die Elemente zweier Arrays in der Reihenfolge zusammenzuführen, zum Beispiel bei der Eingabe von [1,2,3], [4,5,6], Rückgabe [[1,4], [2,5],[3,6]]
Diese Ebene nimmt jedes Mal ein Element aus zwei Arrays, führt es zusammen und fügt es in das Array ein und verarbeitet dann das nächste weiter 1. Führen Sie diesen Vorgang rekursiv aus, bis das Array leer ist.
function zip(target, source) { if (!target.length || !source.length) return []; const [one, ...rest1] = target; const [other, ...rest2] = source; return [[one, other], ...zip(rest1, rest2)]; }
Das Ergebnis ist richtig:
Die erste Ebene ist relativ einfach, dann schauen wir uns die Anforderungen der zweiten Ebene an:
Definieren Sie den ts-Typ für diese Zip-Funktion (zwei Schreibweisen)
Funktion Es gibt zwei Formen der Definition:
Eine Funktion direkt über die Funktion deklarieren:
function func() {}
und eine anonyme Funktion deklarieren und einer Variablen zuweisen:
const func = () => {}
Die Parametertypen und Rückgabewerte sind beide Arrays, aber die spezifischen Typen sind nicht bekannt, daher können Sie „unknown[ ]“ schreiben.
Die Definitionen der beiden Funktionstypen lauten also wie folgt:
Es handelt sich auch um eine direkte Funktion, die den Funktionstyp deklariert, und eine Schnittstelle, die den Funktionstyp deklariert und ihn dann zum Variablentyp hinzufügt.
Da der spezifische Elementtyp nicht bekannt ist, wird unbekannt verwendet.
Sie können hier nach dem Unterschied zwischen „any“ und „unknown“ fragen:
Sowohl „any“ als auch „unknown“ können jeden Typ erhalten:
Aber „any“ kann auch jedem Typ zugeordnet werden, „unknown“ jedoch nicht.
Dies wird nur zum Empfangen anderer Typen verwendet, daher ist „Unbekannt“ angemessener und sicherer als alle anderen.
Diese Ebene ist auch eine relativ einfache ts-Syntax, und die dritte Ebene wird schwieriger:
Verwenden Sie Typprogrammierung, um präzise Typhinweise zu erhalten, z. B. die Übergabe von Parametern an [1,2,3], [4,5, 6 ], dann sollte der Typ des Rückgabewerts [[1,4], [2,5],[3,6]] angeben.
Der Typ des Rückgabewerts muss hier genau sein, und wir müssen ihn bestimmen basierend auf dem Typ des Parameters. Generieren Sie dynamisch Rückgabewerttypen.
Das ist es:
Deklarieren Sie zwei Typparameter Ziel und Quelle, und die Einschränkung ist unbekannt [], was ein Array-Typ eines beliebigen Elementtyps ist.
Diese beiden Typparameter sind die Typen der beiden übergebenen Parameter.
Der Rückgabewert wird über Zip berechnet.
Dann müssen wir den erweiterten Zip-Typ implementieren:
Die übergebenen Typparameter sind zwei Array-Typen, und wir müssen außerdem jedes Element daraus extrahieren und zusammenführen.
Mustervergleich kann zum Extrahieren von Elementen verwendet werden:
Dieser Typ kann also wie folgt definiert werden:
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>] : [] : [];
Extrahieren Sie jeweils das erste Element der beiden Arrays und erstellen Sie ein neues Array. Führen Sie dies dann rekursiv für das verbleibende Array aus, bis das Array leer ist.
Dadurch wird der von uns gewünschte erweiterte Typ erreicht:
Aber wenn Sie ihn als Rückgabewert zur Funktion hinzufügen, wird ein Fehler gemeldet:
Weil Sie nicht wissen, welche Parameter wann sind Sie deklarieren die Funktion natürlich. Der Wert von Zip
Was sollen wir tun?
Kann durch Funktionsüberladung gelöst werden:
ts unterstützt Funktionsüberladung. Sie können Typdefinitionen für mehrere Funktionstypen mit demselben Namen schreiben und schließlich die Implementierung der Funktion schreiben, sodass diese Funktion verwendet wird , es basiert auf dem Typ des Parameters, um den Funktionstypen zu entsprechen.
Die von uns verwendete Typprogrammierung meldet keinen Fehler, wenn sie auf diese Weise geschrieben ist.
Lassen Sie uns einen Blick darauf werfen:
Warum ist der Rückgabewerttyp falsch?
Tatsächlich ist der passende Funktionstyp zu diesem Zeitpunkt korrekt, aber der abgeleitete Typ ist kein Literaltyp.
Sie können zu diesem Zeitpunkt als Konstante hinzufügen.
Aber das Hinzufügen als const wird readonly [1,2,3] ableiten
Die Typen stimmen nicht überein, daher müssen Sie readonly:
zur Deklaration der Typparameter hinzufügenAber Der Typ der Zip-Funktion stimmt erneut nicht überein.
Sollten wir an allen Stellen, an denen dieser Typ verwendet wird, schreibgeschützt hinzufügen?
Keine Notwendigkeit, können wir einfach die schreibgeschützte Änderung entfernen?
Typescript verfügt über einen integrierten erweiterten schreibgeschützten Typ:
Sie können jedem Index des Indextyps schreibgeschützte Änderungen hinzufügen:
Aber es gibt keinen erweiterten Typ, der die schreibgeschützte Änderung entfernen kann es selbst:
Konstruieren Sie einen neuen Indextyp mithilfe der Mapping-Typ-Syntax und fügen Sie -readonly hinzu, um die schreibgeschützte Änderung zu entfernen.
Einige Schüler fragen sich vielleicht: Ist der Array-Typ auch ein Indextyp?
Ja, der Indextyp ist ein Typ, der mehrere Elemente aggregiert, also Objekte, Arrays und Klassen.
Damit wir es natürlich auf Arrays verwenden können:
(Um genau zu sein, nennt man es ein Tupel. Ein Tupel ist ein Array mit einer festen Anzahl von Elementen)
Dann müssen wir nur noch „Nur schreibgeschützt entfernen“ verwenden Veränderbar:
Versuchen Sie es noch einmal:
Fertig! Jetzt ist der Rückgabewerttyp korrekt.
Aber es gibt noch ein anderes Problem, wenn das Literal nicht direkt übergeben wird, kann der Literaltyp nicht abgeleitet werden:
Aber haben wir nicht alle überladene Typen deklariert?
Wenn der Literaltyp nicht abgeleitet werden kann, sollte er mit dieser übereinstimmen:
Aber tatsächlich stimmt er mit dem ersten überein:
Zu diesem Zeitpunkt müssen Sie nur die Reihenfolge der beiden Funktionen ändern Typen. Jetzt:
Die Situation der Literalparameter ist derzeit noch korrekt:
Warum?
Weil die Typen überladener Funktionen von oben nach unten übereinstimmen. Solange einer übereinstimmt, wird er angewendet.
Bei nicht-literalen Werten ist der Typ Zahl[], der mit dem Typ unbekannt[] übereinstimmen kann, sodass dieser Funktionstyp wirksam wird.
Bei Literalen ist die Ableitung schreibgeschützt [1,2,3], mit schreibgeschützt, sodass sie nicht mit „unknown[] übereinstimmt. Wenn Sie weiterhin übereinstimmen, werden Sie den Funktionstyp mit Typparametern abgleichen. .
Auf diese Weise wird in beiden Fällen der entsprechende Funktionstyp angewendet.
Der gesamte Code sieht so aus:
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, '5', 6]; const result2 = zip(arr1, arr2);
今天我们做了一道综合的 ts 面试题,一共有三层:
第一层实现 js 的逻辑,用递归或者循环都能实现。
第二层给函数加上类型,用 function 声明类型和 interface 声明函数类型两种方式,参数和返回值都是 unknown[]。
第三层是用类型编程实现精准的类型提示,这一层需要拿到参数的类型,通过提取元素的类型并构造出新的数组类型返回。还要通过函数重载的方式来声明类型,并且要注意重载类型的声明顺序。
as const 能够让字面量推导出字面量类型,但会带有 readonly 修饰,可以自己写映射类型来去掉这个修饰。
其实这也是我们学习 ts 的顺序,我们先要能把 js 逻辑写出来,然后知道怎么给函数、class 等加 ts 类型,之后学习类型编程,知道怎么动态生成类型。
其中类型编程是 ts 最难的部分,也是最强大的部分。攻克了这一层,ts 就可以说学的差不多了。
【相关推荐:javascript学习教程
Das obige ist der detaillierte Inhalt vonTeilen Sie eine gute TS-Interviewfrage (einschließlich 3 Ebenen) und sehen Sie, welche Ebene Sie beantworten können!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!