использоватьTypeScript
Прошло некоторое время, и это действительно хорошая вещь, хотя я нашел кое-что в процессе его использования.bug
, но есть небольшие проблемы, так что общее впечатление все еще очень хорошее.
TypeScript
почему это называетсяType
, неотделим от его строгой типизации, которая также отличается отJavaScript
Наиболее важным моментом является то, что объявление типа может быть записано непосредственно в коде или может быть написан отдельный файл описания для представления типа.*.d.ts
.
общий путь
первый вd.ts
В файле объявления не будет каких-то простых определений базовых типов (поскольку они записываются после выражений и переменных, а определения здесь не имеют смысла), а типы, определенные в файлах объявлений, часто представляют собой сложные структуры.
Большая часть грамматики такая же, как и в обычномts
Синтаксис в файле тоже соответствуетexport
Далее следуют элементы для экспорта.
Проще всего использоватьtype
ключевое слово для определения:
type A = { // 定义复杂结构
b: number
c: string
}
type Func = () => number // 定义函数
type Key = number | string // 多个类型
Тип комбинации
И вTypeScript
Есть очень простой способ нацелитьсяtype
Например, для повторного использования у нас естьAnimal
тип, аDog
тип, вы можете использовать&
для повторного использования.
P.S> &
Символы могут быть объединены в несколько
type Animal = {
weight: number
height: number
}
type Dog = Animal & {
leg: number
}
Динамическое назначение типа JSON
если у нас естьJSON
структуру, и ееkey
является динамическим, то мы, конечно, не можем объединить всеkey
Все прописано в коде, нам нужно только просто указать подстановочный знак:
type info = {
[k: string]: string | number // 可以指定多个类型
}
const infos: info = {
a: 1,
b: '2',
c: true, // error 类型不匹配
}
А в новой версии больше рекомендуется использовать встроенные функцииRecord
реализовать:
const infos: Record<string, string | number> = {
a: 1,
b: '2',
c: true, // error
}
Получить тип переменной
Предположим, у нас есть объект JSON, который содержитname
,age
два свойства, мы можем передать некоторыеTypeScript
Встроенные служебные функции, чтобы делать некоторые интересные вещи.
пройти черезkeyof
а такжеtypeof
Комбинируя, чтобы получить желаемый результат:
const obj = {
name: 'Niko',
age: 18
}
// 如果是这样的取值,只能写在代码中,不能写在 d.ts 文件中,因为声明文件里边不能存在实际有效的代码
type keys = keyof typeof obj
let a: keys = 'name' // pass
let b: keys = 'age' // pass
let c: keys = 'test' // error
И если мы хотим преобразовать неоднородный типJSON
Изменено на единый типJSON
Вы также можете использовать этот способ:
const obj = {
name: 'Niko',
age: 18,
birthday: new Date()
}
const infos: Record<keyof typeof obj, string> = {
name: '',
age: '',
birthday: 123, // 出错,提示类型不匹配
test: '', // 提示不是`info`的已知类型
}
Получить возвращаемый тип функции
Допустим, у нас есть функция, которая возвращаетJSON
, а нам это нужноJSON
быть типом.
затем черезReturnType<>
реализовать:
function func () {
return {
name: 'Niko',
age: 18
}
}
type results = ReturnType<typeof func>
// 或者也可以拼接 keyof 获取所有的 key
type resultKeys = keyof ReturnType<typeof func>
// 亦或者可以放在`Object`中作为动态的`key`存在
type infoJson = Record<keyof ReturnType<typeof func>, string>
объявить функции в коде иclass
Типы
потому что мы знаем, что функция иclass
На момент создания есть актуальный код (тело функции, конструктор).
Но мы пишемd.ts
В файле объявления это просто типоспецифичное ограничение, так что реального кода точно не будет, но если в обычномts
Записывать это в файл будет неправильно, поэтому для такой ситуации нам нужно использоватьdeclare
Ключевое слово означает, что мы здесь, чтобы определить тип, а не объект или функцию:
class Personal {
name: string
// ^ 出错了,提示`name`必须显式的进行初始化
}
function getName (personal: Personal): name
// ^ 出错了,提示函数缺失实现
Правильное использование выглядит следующим образом:
-declare class Personal {
+declare class Personal {
name: string
}
-function getName (personal: Personal): name
+declare function getName (personal: Personal): name
Конечно, это определение обычно не рекомендуется.class
Да, вы должны использоватьinterface
заменить вот такclass
должны существовать только для не-TS
Описание модуля, если это модуль собственной разработки, то сама структура имеет признаки объявления типа.
перегрузка функций
Эта концепция доступна только в некоторых строго типизированных языках.TypeScript
, это также строго типизированный язык, поэтому будут места, где необходимо использовать такие объявления.
Например, у нас естьadd
функция, которая может получитьstring
Параметры типа сращиваются, а также могут получатьnumber
Добавляются параметры типа.
Следует отметить, что его можно разместить только в определении перегрузки функции стороннего подключаемого модуля.d.ts
файл, в других средах рекомендуется объединить определение и реализацию функции (хотя конфигурацияpaths
Отдельная обработка также может быть достигнута, но тогда ограничения на создание функции теряются)
// index.ts
// 上边是声明
function add (arg1: string, arg2: string): string
function add (arg1: number, arg2: number): number
// 因为我们在下边有具体函数的实现,所以这里并不需要添加 declare 关键字
// 下边是实现
function add (arg1: string | number, arg2: string | number) {
// 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 arg1 + arg2
if (typeof arg1 === 'string' && typeof arg2 === 'string') {
return arg1 + arg2
} else if (typeof arg1 === 'number' && typeof arg2 === 'number') {
return arg1 + arg2
}
}
TypeScript
Перегрузка функции — это просто объявление нескольких функций, и конкретная логика должна быть написана вами самостоятельно, так как она не будет объединять тела ваших нескольких функций с одним и тем же именем.
Порядок нескольких функций
Представьте, если бы у нас была функция, которая передается вDate
параметр типа, вернуть егоunix
временная метка, если она переданаObject
, то конкретный тип объектаtoString
вывод и прямой возврат в других случаях Как должна быть написана такая функция?
Просто для примера демонстрация, нормальные люди такую функцию не напишут...
function build (arg: any) {
if (arg instanceof Date) {
return arg.valueOf()
} else if (typeof arg === 'object') {
return Object.prototype.toString.call(arg)
} else {
return arg
}
}
Однако такая перегрузка функций очень требовательна к порядку объявления, и функция с высокой точностью должна быть помещена первой:
// 这样是一个错误的示例,因为无论怎样调用,返回值都会是`any`类型
function build(arg: any): any
function build(arg: Object): string
function build(arg: Date): number
потому чтоTypeScript
Найдя объявление о перегрузке функции, она остановится и не будет продолжать поиск.any
является наиболее неоднозначным диапазоном, в то время какObject
содержит сноваDate
, поэтому мы должны расположить их в порядке от меньшего к большему:
function build(arg: Date): number
function build(arg: Object): string
function build(arg: any): any
// 这样在使用的时候才能得到正确的类型提示
const res1 = build(new Date()) // number
const res2 = build(() => { }) // string
const res3 = build(true) // any
Некоторые сценарии, не требующие перегрузки функций
Смысл перегрузки функции в том, чтобы вы знали, что разные параметры передаются для получения разных результатов.Если переданные параметры разные, полученные результаты (Типы), но то же самое, то не используйте здесь перегрузку функций (это не имеет смысла).
Если возвращаемый тип функции одинаков, то нет необходимости использовать перегрузку функции.
function func (a: number): number
function func (a: number, b: number): number
// 像这样的是参数个数的区别,我们可以使用可选参数来代替函数重载的定义
function func (a: number, b?: number): number
// 注意第二个参数在类型前边多了一个`?`
// 亦或是一些参数类型的区别导致的
function func (a: number): number
function func (a: string): number
// 这时我们应该使用联合类型来代替函数重载
function func (a: number | string): number
Interface
interface
вTypeScript
уникальный вJavaScript
нисколькоinterface
Скажи это.
потому чтоinterface
используется только для уточнения его реализацииclass
Соответствующее поведение без какого-либо реального кода является недопустимой операцией для языка сценариев.
грамматически сclass
не большая разница, но вinterface
Только свойства-члены могут быть объявлены вfunction
Вы можете писать только конкретные полученные параметры и тип возвращаемого значения, а не вinterface
Напишите конкретное тело функции вinterface
Назначение в:
// 这是一个错误的示例
interface PersonalIntl {
name: string = 'Niko'
sayHi (): string {
return this.name
}
}
// 在 interface 中只能存在类型声明
interface PersonalIntl {
name: string
sayHi (): string
}
Действительно, в некоторых случаях используютinterface
с обычнымtype
Определения не имеют значения.
Например, мы хотим экспортировать существованиеname
а такжеage
Объект с двумя свойствами:
// types/personal.d.ts
export interface PersonalIntl {
name: string
age: number
}
// index.d.ts
import { PersonalIntl } from './types/personal'
const personal: PersonalIntl = {
name: 'Niko',
age: 18,
}
еслиinterface
заменитьtype
С определением тоже все в порядке:
// types/personal.d.ts
export type PersonalIntl = {
name: string
age: number
}
Такое определение прекрасно подходит для использования на основе вышеизложенного, но оно применимо только кObject
Буквальное объявление, нет способа хорошо ограничить егоclass
режим, поэтому мы используемinterface
ограничиватьclass
Реализация:
import { PersonalIntl } from './types/personal'
class Personal implements PersonalIntl {
constructor(public name: string, public age: number) { }
// 上边的简写与下述代码效果一致
public name: string
public age: number
constructor (name: string, age: number) {
this.name = name
this.age = age
}
}
const personal = new Personal('niko', 18)
Некоторые сомнения по поводу объявлений функций-членов
Во-первых, есть два способа определить функцию в интерфейсе: один в экземпляре и один в цепочке прототипов.
Два объявления выглядят следующим образом:
interface PersonalIntl {
func1 (): any // 原型链方法
func2: () => any // 实例属性
}
Но когда мы реализуем эти два свойства, мы можем фактически конвертировать друг друга, и нет строгого требования, какой метод должен использоваться:
class Personal implements PersonalIntl {
func1 () {
console.log(this)
}
func2 = () => {
console.log(this)
}
}
На самом деле, после компиляции двухJavaScript
Есть разница в коде, не понятно что этоbug
Или конструкция такая, структура такая:
var Personal = /** @class */ (function () {
function Personal() {
var _this = this;
this.func2 = function () {
console.log(_this);
};
}
Personal.prototype.func1 = function () {
console.log(this);
};
return Personal;
}());
Поэтому рекомендуется соблюдатьinterface
Определенный способ создания, позволяющий избежать некоторых странных проблем, которые могут существовать.
Автоматическое слияние объявлений интерфейса
потому чтоinterface
даTypeScript
Уникальный, так что также будут некоторые интересные функции, такие как то же имяinterface
будут объединены автоматически:
interface PersonalIntl {
name: string
}
interface PersonalIntl {
age: number
}
class Personal implements PersonalIntl {
name = 'Niko'
age = 18
}
Не используйте перегрузку функций в интерфейсах
существуетinterface
Если вы используете перегрузку функций вbuild
функция, еслиinterface
объявлено в , а затем вclass
Реализовано, то как бы ни вызывался, будет учитываться тип возвращаемого значенияany
.
Итак, правильный путьclass
Объявите о перегрузках вclass
реализовано вinterface
определить не более одногоany
, вместо трех перегрузок.
class Util implements UtilIntl {
build(arg: Date): number
build(arg: Object): string
build(arg: any): any
build(arg: any) {
if (arg instanceof Date) {
return arg.valueOf()
} else if (typeof arg === 'object') {
return Object.prototype.toString.call(arg)
} else {
return arg
}
}
}
резюме
СвязанныйTypeScript
Объявления типа объявления, относящиеся к этим наиболее часто используемым, в настоящее время суммированы, и друзья могут добавить их.
существовал в предыдущих версияхmodule
а такжеnamespace
Определение , но в настоящее время кажется, что более рекомендуется версия ES-Modules.import
/export
Для достижения аналогичных функций, а не пользовательского синтаксиса, поэтому описание этих двух ключевых слов пропускается.
В официальной документации есть шаблон того, как написать файл объявления, вы можете обратиться к:массив телепортации