TypeScript можно понимать как надмножество JavaScript, что означает, что он охватывает все функции JavaScript и имеет собственный уникальный синтаксис поверх него.
Недавно новый проект начал пошаговое путешествие TS, теперь он делится некоторыми процедурами, которые могут быть нарисованы для всех.
Почему выбирают ТС
Будучи статическим компилируемым языком со строгой типизацией, созданным гигантской компанией, этот язык существует уже несколько лет, и я считаю, что это очень стабильный язык, поддерживаемый сообществом.
Мы знаем, что JavaScript — это динамический слабо типизированный интерпретируемый скриптовый язык, а динамический язык приносит много удобства.Мы можем изменять тип переменной по желанию для достижения желаемой цели во время выполнения кода.
Но в то же время это палка о двух концах: когда перед вами появляется огромный проект и сталкивается с чрезвычайно сложной логикой, вам сложно понять, что это за переменная и что делать с кодом. Может случайно наступить на яму.
Статическая строго типизированная компиляция может принести много преимуществ, наиболее важным из которых является то, что она может помочь разработчикам избежать некоторых небрежных проблем:
На картинке показана десятка аномалий с наибольшим количеством тысяч проектов, подсчитанных роллбаром.
Нетрудно заметить, что исключений больше, чем вы осмеливаетесь допустить из-за несоответствия типов и пустых переменных.
Например
И это было значительно улучшено в TS.Любая ссылка на переменную должна указывать свой собственный тип, а то, что вы можете использовать в коде ниже, и какие методы поддерживаются, должно быть определено выше:
Это приглашение будет выдаваться разработчику в период разработки и компиляции, чтобы избежать проблем, обнаруженных после подключения к сети и последующего изменения.
Еще одним преимуществом статически скомпилированных типов являются сигнатуры функций.
Или, как упоминалось выше, поскольку это динамический язык сценариев, редактору сложно правильно сказать вам, какие параметры нужно передать функции, которую вы хотите вызвать во время разработки, и какой тип возвращаемого значения функция вернет.
В TS для функции сначала нужно определить типы всех параметров и тип возвращаемого значения.
Таким образом, когда функция вызывается, мы можем ясно видеть эффект этой функции:
Это две самые основные функции, которые могут сделать программу более стабильной.Конечно, функций в TS больше:TypeScript | Handbook
Применение TypeScript в узле
На официальном сайте ТС имеется большое количествоПример, в котором нашлиExpressНемного модифицированный для этого пример версии, примененный в проекте koa.
зависит от окружающей среды
Перед использованием TS вам необходимо подготовить следующие вещи:
- VS code, также производимый гигантской компанией, он разработан самой TS, поэтому в настоящее время этот редактор имеет самую высокую поддержку для TS.
- Node.js рекомендует версию 8.11 или выше.
-
npm i -g typescript
, установите TS глобально, команда tsc, используемая для компиляции, находится здесь -
npm i -g nodemon
, установить nodemon глобально, автоматически обновить серверную программу после компиляции tsc
И некоторые основные зависимости, используемые в проекте:
-
reflect-metadata
: базовый пакет, от которого зависят многие пакеты декораторов для вставки данных. -
routing-controllers
: Используйте декораторы для разработки koa-router. -
sequelize
: абстрагированные операции с базой данных -
sequelize-typescript
: версия вышеупомянутого плагина Decorator, используемая при определении сущностей.
Структура проекта
Во-первых, освободите структуру текущего проекта:
.
├── README.md
├── copy-static-assets.ts
├── nodemon.json
├── package-lock.json
├── package.json
├── dist
├── src
│ ├── config
│ ├── controllers
│ ├── entity
│ ├── models
│ ├── middleware
│ ├── public
│ ├── app.ts
│ ├── server.ts
│ ├── types
│ └── utils
├── tsconfig.json
└── tslint.json
src
Для основного каталога разработки все коды TS находятся здесь.После компиляции он сгенерируетsrc
тот же уровеньdist
папка, эта папкаnode
Код, который на самом деле запускает движок.
существуетsrc
Ниже основной код разделен на следующую структуру (добавляйте и удаляйте в соответствии с реальной ситуацией вашего собственного проекта):
# | folder | desc |
---|---|---|
1 | controllers |
Используется для обработки интерфейсных запросов, исходныйapps ,routes папка. |
2 | middleware |
Хранит различное промежуточное ПО, глобальное или пользовательское промежуточное ПО. |
3 | config |
Расположение различных элементов конфигурации, включая порты,log Постоянные определения для путей, различные балалы. |
4 | entity |
Здесь хранятся все определения сущностей (используя sequenceize для операций с базой данных). |
5 | models |
использовать отentity сущность вsequelize для завершения операции инициализации иsequelize Броски предметов. |
6 | utils |
Сохраненные общедоступные функции, извлеченные из различных ежедневных разработок |
7 | types |
Хранит определения различных настраиваемых составных типов, а также определения различных структур, атрибутов и возвращаемых значений методов (в настоящее время включая широко используемые версии Promise для redis и qconf). |
controllers
Контроллеры отвечают только за логику обработки, добавление, удаление, изменение и проверку данных, манипулируя объектом модели, а не базой данных.
Учитывая, что большинство версий Node-проектов компании были обновлены доNode 8.11
, и это правильно, мы попробуем новый синтаксис.
То есть мы откажемсяGenerator
,Объятиеasync
/await
.
использоватьKoa
,Express
Дети, которые писали интерфейсы, должны знать, что когда проект становится огромным, он на самом деле генерирует много повторяющегося нелогичного кода:
router.get('/', ctx => {})
router.get('/page1', ctx => {})
router.get('/page2', ctx => {})
router.get('/page3', ctx => {})
router.get('/pageN', ctx => {})
В каждом мониторинге маршрута выполняется много повторяющейся работы:
router.get('/', ctx => {
let uid = Number(ctx.cookies.get('uid'))
let device = ctx.headers['device'] || 'ios'
let { tel, name } = ctx.query
})
Заголовок почти каждого маршрута выполняет работу по получению параметров, и параметры, скорее всего, поступают изheader
,body
Четноеcookie
а такжеquery
.
Поэтому мы внесли серьезные изменения в первоначальное использование koa и использовалиrouting-controllersМножество декораторов приложений, помогающих нам справиться с большей частью нелогического кода.
Определение оригинального роутера:
module.exports = function (router) {
router.get('/', function* (next) {
let uid = Number(this.cookies.get('uid'))
let device = this.headers['device']
this.body = {
code: 200
}
})
}
Определение с использованием TypeScript и декораторов:
@Controller
export default class {
@Get('/')
async index (
@CookieParam('uid') uid: number,
@HeaderParam('device') device: string
) {
return {
code: 200
}
}
}
Чтобы сделать интерфейс более удобным и понятным, мы отказались от исходногоbd-router
функция (согласно пути к файлу как пути к интерфейсу, путь к файлу в TS используется только для наслоения файлов).
прямо вcontrollers
Соответствующий интерфейс объявлен в файле ниже для мониторинга.
middleware
Если это глобальное промежуточное ПО, добавьте его непосредственно в класс.@Middleware
декоратор и наборtype: 'after|before'
Вот и все.
Если это какое-то конкретное промежуточное ПО, просто создайте общий класс, а затем используйтеcontroller
указано на объекте@UseBefore
/@UseAfter
(Это может быть написано в классе или в методе).
Все промежуточное ПО должно наследовать соответствующий интерфейс MiddlewareInterface и реализовыватьuse
метод
// middleware/xxx.ts
import {ExpressMiddlewareInterface} from "../../src/driver/express/ExpressMiddlewareInterface"
export class CompressionMiddleware implements KoaMiddlewareInterface {
use(request: any, response: any, next?: Function): any {
console.log("hello compression ...")
next()
}
}
// controllers/xxx.ts
@UseBefore(CompressionMiddleware)
export default class { }
entity
Файл отвечает только за определение модели данных и не выполняет никаких логических операций.
Точно так же, как и с помощью декоратора sequenceize+, сущность используется только для создания модели данных для связи с базой данных.
import { Model, Table, Column } from 'sequelize-typescript'
@Table({
tableName: 'user_info_test'
})
export default class UserInfo extends Model<UserInfo> {
@Column({
comment: '自增ID',
autoIncrement: true,
primaryKey: true
})
uid: number
@Column({
comment: '姓名'
})
name: string
@Column({
comment: '年龄',
defaultValue: 0
})
age: number
@Column({
comment: '性别'
})
gender: number
}
Так как для продолжения требуется также соответствующий адрес базы данных, учетная запись, пароль, база данных и другая информация для установления соединения, рекомендуется поместить все объекты одной и той же базы данных в каталог, чтобы упростить загрузку соответствующей модели.
Рекомендуется создать соответствующую информацию о конфигурации в разделе config и добавить столбец для хранения ключа сущности.
Таким образом, когда связь с базой данных установлена и модель данных загружена, все объекты по этому пути могут быть динамически импортированы:
// config.ts
export const config = {
// ...
mysql1: {
// ... config
+ entity: 'entity1' // 添加一列用来标识是什么实体的key
},
mysql2: {
// ... config
+ entity: 'entity2' // 添加一列用来标识是什么实体的key
}
// ...
}
// utils/mysql.ts
new Sequelize({
// ...
modelPath: [path.reolve(__dirname, `../entity/${config.mysql1.entity}`)]
// ...
})
model
Позиционирование модели заключается в создании абстрактного объекта базы данных на основе соответствующей сущности.Поскольку используется сиквелизация, файлы в этом каталоге станут очень краткими.
В основном просто инициализируйте объект продолжения и выбросьте его после загрузки модели.
export default new Sequelize({
host: '127.0.0.1',
database: 'database',
username: 'user',
password: 'password',
dialect: 'mysql', // 或者一些其他的数据库
modelPaths: [path.resolve(__dirname, `../entity/${configs.mysql1.entity}`)], // 加载我们的实体
pool: { // 连接池的一些相关配置
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
operatorsAliases: false,
logging: true // true会在控制台打印每次sequelize操作时对应的SQL命令
})
utils
Все общественные функции размещены здесь.
При этом рекомендуется написать соответствующий индексный файл (index.ts), общий формат которого следующий:
// utils/get-uid.ts
export default function (): number {
return 123
}
// utils/number-comma.ts
export default function(): string {
return '1,234'
}
// utils/index.ts
export {default as getUid} from './get-uid'
export {default as numberComma} from './number-comma'
каждый раз новыйutil
, просто идиindex
Добавьте соответствующий индекс вutils
:
import {getUid, numberComma} from './utils'
configs
Configs хранит различную информацию о конфигурации, включая URL-адреса некоторых сторонних интерфейсов, конфигурации базы данных и пути журналов.
Статические данные для различных балабал.
Если файлов конфигурации много, рекомендуется разбить их на несколько файлов, а затем следоватьutils
способ написать индексный файл.
types
Здесь хранятся все пользовательские определения типов, которые не предоставляются некоторыми сообществами с открытым исходным кодом, но сторонние плагины, которые мы используем, должны быть определены здесь. Нет поддержки TS, например той, которую мы использовалиnode-qconf
:
// types/node-qconf.d.ts
export function getConf(path: string): string | null
export function getBatchKeys(path: string): string[] | null
export function getBatchConf(path: string): string | null
export function getAllHost(path: string): string[] | null
export function getHost(path: string): string | null
В файле определения типа указано, что суффикс .d.ts
На все файлы в типах можно ссылаться напрямую, не заботясь об относительных путях (другие обычные модели должны записывать относительные пути, что является неприятной проблемой).
Некоторые проблемы с использованием TS в настоящее время
В текущем репозитории GitHub есть более 2600 открытых проблем, и после фильтрации тегов ошибок остается еще 900+ проблем.
Поэтому сложно гарантировать, что в процессе использования не будет подводных камней, но проект с таким количеством активных вопросов также может объяснить популярность этого проекта со стороны.
Единственная неловкая проблема, с которой я столкнулся до сих пор, это:Путь к справочному файлу должен быть указан полностью. .
import module from '../../../../f**k-module'
резюме
Когда я впервые попробовал TypeScript, я влюбился в язык, хотя и будут небольшие проблемы, но я все равно смогу их преодолеть :).
Использование статически строго типизированного скомпилированного языка может устранить многие ошибки во время разработки.
Простой пример, основанный на приведенном выше описании:репозиторий кода
Я надеюсь, что вам всем весело, и если у вас есть какие-либо вопросы, связанные с TS, пожалуйста, не стесняйтесь беспокоить их.NPM loves U.
.