Следующие вопросы возникли в результате обсуждений с небольшими партнерами компании и пользователями сети и разбиты на главы в надежде предложить другой способ мышления (чтобы не наступить на яму) для решения проблемы.
перегрузка функций
TypeScript предоставляет функцию перегрузки функций, которая используется для работы со сценариями использования разных типов возврата из-за разных параметров функции.При его использовании вам нужно только определить несколько типов для одной и той же функции.Простое использование заключается в следующем:
declare function test(a: number): number;
declare function test(a: string): string;
const resS = test('Hello World'); // resS 被推断出类型为 string;
const resN = test(1234); // resN 被推断出类型为 number;
Он также подходит для сценариев с разными параметрами и одним и тем же типом возвращаемого значения, нам нужно только знать, какие параметры можно использовать при определении типа функции.
Рассмотрим следующий пример:
interface User {
name: string;
age: number;
}
declare function test(para: User | number, flag?: boolean): number;
на этоtest
В функции нашим намерением может быть передача параметровpara
даUser
время, не передаватьflag
, когда входящийpara
даnumber
при проходеflag
. TypeScript этого не знает, когда вы проходитеpara
заUser
час,flag
Также позволяет проходить:
const user = {
name: 'Jack',
age: 666
}
// 没有报错,但是与想法违背
const res = test(user, false);
Использование перегрузки функций может помочь нам достичь:
interface User {
name: string;
age: number;
}
declare function test(para: User): number;
declare function test(para: number, flag: boolean): number;
const user = {
name: 'Jack',
age: 666
};
// bingo
// Error: 参数不匹配
const res = test(user, false);
В реальных проектах вам, возможно, придется написать еще несколько шагов, например, вclass
середина:
interface User {
name: string;
age: number;
}
const user = {
name: 'Jack',
age: 123
};
class SomeClass {
/**
* 注释 1
*/
public test(para: User): number;
/**
* 注释 2
*/
public test(para: number, flag: boolean): number;
public test(para: User | number, flag?: boolean): number {
// 具体实现
return 11;
}
}
const someClass = new SomeClass();
// ok
someClass.test(user);
someClass.test(123, false);
// Error
someClass.test(123);
someClass.test(user, false);
тип карты
С тех пор, как в TypeScript 2.1 появились сопоставленные типы, он постоянно улучшался и совершенствовался. В версии 2.1 можно пройтиkeyof
получить объектkey
Тип, встроенныйPartial
,Readonly
,Record
,Pick
Тип отображения; добавлено в версии 2.3.ThisType
; Добавлено в версии 2.8Exclude
,Extract
,NonNullable
,ReturnType
,InstanceType
; также в этом релизе добавлены типы условий и улучшенияkeyof
; версия 3.1 поддерживает отображение кортежей и массивов. Все это означает, что сопоставленные типы играют ключевую роль в TypeScript.
вThisType
не фигурирует в официальной документации, в основном используется для ввода литералов объектовthis
:
// Compile with --noImplicitThis
type ObjectDescriptor<D, M> = {
data?: D;
methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M
}
function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
let data: object = desc.data || {};
let methods: object = desc.methods || {};
return { ...data, ...methods } as D & M;
}
let obj = makeObject({
data: { x: 0, y: 0 },
methods: {
moveBy(dx: number, dy: number) {
this.x += dx; // Strongly typed this
this.y += dy; // Strongly typed this
}
}
});
obj.x = 10;
obj.y = 20;
obj.moveBy(5, 5);
Это из-за
ThisType
Появление Vue 2.5 позволило улучшить поддержку TypeScript.
Хотя существует множество встроенных типов сопоставления, во многих случаях нам необходимо настроить тип сопоставления в соответствии с нашим собственным проектом:
Например, вы можете захотеть извлечь тип функции из типа интерфейса:
type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;
interface Part {
id: number;
name: string;
subparts: Part[];
updatePart(newName: string): void;
}
type T40 = FunctionPropertyNames<Part>; // "updatePart"
type T42 = FunctionProperties<Part>; // { updatePart(newName: string): void }
Например, вы можете использовать псевдоним для метода, принадлежащего определенному атрибуту, в других местах для удобства.
Например:SomeClass
следующее свойствоvalue = [1, 2, 3]
, возможно, вы добавили эту функциональность в свой класс в Decorators:SomeClass
вызыватьthis.find()
когда на самом деле звонюthis.value.find()
, но TypeScript не знает об этом на данный момент:
class SomeClass {
value = [1, 2, 3];
someMethod() {
this.value.find(/* ... */); // ok
this.find(/* ... */); // Error:SomeClass 没有 find 方法。
}
}
С помощью типов отображения иinterface + class
Способ объявления может достичь нашей цели:
type ArrayMethodName = 'filter' | 'forEach' | 'find';
type SelectArrayMethod<T> = {
[K in ArrayMethodName]: Array<T>[K]
}
interface SomeClass extends SelectArrayMethod<number> {}
class SomeClass {
value = [1, 2, 3];
someMethod() {
this.forEach(/* ... */) // ok
this.find(/* ... */) // ok
this.filter(/* ... */) // ok
this.value // ok
this.someMethod() // ok
}
}
const someClass = new SomeClass();
someClass.forEach(/* ... */) // ok
someClass.find(/* ... */) // ok
someClass.filter(/* ... */) // ok
someClass.value // ok
someClass.someMethod() // ok
экспорт
SomeClass
также можно использовать класс.
Может быть немного не хватает, в этом кодеinterface SomeClass extends SelectArrayMethod<number> {}
Вам нужно вручную добавить конкретный тип дженерика (лучшего способа я пока не придумал).
утверждение типа
Утверждения типа используются для явного указания TypeScript подробного типа значения, и разумное использование может снизить нашу рабочую нагрузку.
Например, переменная не имеет начального значения, но мы знаем информацию о ее типе (она может быть возвращена из бэкенда), можно ли как-то правильно вывести информацию о типе и нормально работать? В Интернете рекомендуется установить начальное значение, а затем использоватьtypeof
Получить тип (может использоваться в другом месте). Однако я могу быть ленивым и не люблю задавать начальные значения, в этом случае использование утверждений типа может решить такие проблемы:
interface User {
name: string;
age: number;
}
export default class NewRoom extends Vue {
private user = {} as User;
}
При настройке инициализации, добавлении утверждений нам не нужно добавлять начальные значения, и редактор может нормально давать подсказки по коду. еслиuser
Есть много атрибутов, которые могут решить много ненужной работы, определениеinterface
Также можно использовать в другом месте.
тип перечисления
Типы перечисления делятся на числовые типы и строковые типы, где перечисления числовых типов могут использоваться в качестве флагов:
// https://github.com/Microsoft/TypeScript/blob/master/src/compiler/types.ts#L3859
export const enum ObjectFlags {
Class = 1 << 0, // Class
Interface = 1 << 1, // Interface
Reference = 1 << 2, // Generic type reference
Tuple = 1 << 3, // Synthesized generic tuple type
Anonymous = 1 << 4, // Anonymous
Mapped = 1 << 5, // Mapped
Instantiated = 1 << 6, // Instantiated anonymous or mapped type
ObjectLiteral = 1 << 7, // Originates in an object literal
EvolvingArray = 1 << 8, // Evolving array type
ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties
ContainsSpread = 1 << 10, // Object literal contains spread operation
ReverseMapped = 1 << 11, // Object contains a property from a reverse-mapped type
JsxAttributes = 1 << 12, // Jsx attributes type
MarkerType = 1 << 13, // Marker type used for variance probing
JSLiteral = 1 << 14, // Object type declared in JS - disables errors on read/write of nonexisting members
ClassOrInterface = Class | Interface
}
на машинописном языкеsrc/compiler/types
В исходном коде определено большое количество константных перечислений, основанных на числовых типах, как показано выше.Это эффективный способ хранения и представления коллекций логических значений..
существует«Глубокое понимание TypeScript»Есть пример использования:
enum AnimalFlags {
None = 0,
HasClaws = 1 << 0,
CanFly = 1 << 1,
HasClawsOrCanFly = HasClaws | CanFly
}
interface Animal {
flags: AnimalFlags;
[key: string]: any;
}
function printAnimalAbilities(animal: Animal) {
var animalFlags = animal.flags;
if (animalFlags & AnimalFlags.HasClaws) {
console.log('animal has claws');
}
if (animalFlags & AnimalFlags.CanFly) {
console.log('animal can fly');
}
if (animalFlags == AnimalFlags.None) {
console.log('nothing');
}
}
var animal = { flags: AnimalFlags.None };
printAnimalAbilities(animal); // nothing
animal.flags |= AnimalFlags.HasClaws;
printAnimalAbilities(animal); // animal has claws
animal.flags &= ~AnimalFlags.HasClaws;
printAnimalAbilities(animal); // nothing
animal.flags |= AnimalFlags.HasClaws | AnimalFlags.CanFly;
printAnimalAbilities(animal); // animal has claws, animal can fly
в коде выше|=
используется для добавления флага,&=
и~
убрать флаг,|
Используется для объединения флагов.