Рубрики
Без рубрики

Узнайте, как внести свой вклад в компилятор TypeScript на GitHub через реальный пример

Узнайте, как внести свой вклад в компилятор TypeScript на GitHub через реальный пример. Tagged с помощью TypeScript, OpenSource, Compilers, JavaScript.

Несколько дней назад мне удалось отправить свой первый пиар в проект TypeScript на GitHub. Это то, что я пытался сделать в течение очень долгого времени, но, к сожалению, это было слишком сложным для меня.

Я решил написать этот пост, потому что я на 100% уверен, что есть много людей, которые чувствуют себя такими же, как я. Я попытаюсь описать, как я лично нашел способ внести свой вклад. Моя цель – надеяться, что помочь другим также внести свой вклад.

Обратите внимание, что я не эксперт в интернатах компилятора TypeScript. Пожалуйста, поправьте меня, используя комментарии ниже, если я скажу, что неверно.

Предварительные условия

Я собираюсь принять несколько вещей в этой статье:

  • Вы понимаете Git и уже установил его на машине.
  • Вы понимаете TypeScript и использовал его какое -то время.
  • Вы понимаете Поток GitHub И у вас уже есть GitHub учетная запись.

Настоящий пример

Я собираюсь использовать реальную проблему и ее вклад в качестве ссылки во время этого поста. Пожалуйста, обратитесь к следующим ссылкам, если вы хотите увидеть исходную проблему и вклад:

О кривой обучения

Как я уже сказал, внесение вкладчиков в TypeScript – это то, что я пытался сделать в течение очень долгого времени, но, к сожалению, это было слишком сложным для меня. В этом разделе я постараюсь описать свое личное обучение.

Я лично считаю, что участие в проекте с открытым исходным кодом, таким как TypeScript, не то, чего мы можем достичь всего за несколько дней. Впереди нас давняя кривая обучения, но каждый может внести свой вклад в правильное количество усилий и настойчивости.

Моя личная кривая обучения началась несколько лет назад, когда я создал свою первую учетную запись Github, и я начал работать над своими собственными проектами. Этими проектами были просто изучение упражнений и демонстрационных приложений, но это помогло мне познакомиться с GitHub и Git.

Тогда у меня был сильный интерес к TypeScript, и я писал книга об этом . Это привело меня к тому, чтобы посетить дорожную карту Typescript, проблемы TypeScript и другие проекты TypeScript, такие как DeganipeDpect on Github. Я читал сотни проблем, PR и комментариев в течение длительного периода времени.

Через некоторое время я начал вносить свой вклад в Определеннотипед Анкет Я начал с сообщений о проблемах, но в итоге я отправил несколько PRS. Мои самые первые PRS были изменения документации, обновления зависимостей и некоторые очень простые исправления ошибок. В конце концов, я закончил тем, что создал новые определения типа и документировал свой опыт в Другая статья Анкет

Использование как TypeScript, так и GitHub заставит меня очень хорошо понять TypeScript, GIT и GitHub Но я все еще не смог внести вклад компилятора TypeScript. Основная проблема заключалась в том, что я работал над некоторыми библиотеками, такими как Inversifyjs и некоторые веб -приложения с React и Node.js, но эти проекты сильно отличаются от компилятора TypeScript.

Как я могу узнать о компиляторе TypeScript?

Поначалу вклад в компилятор может быть немного страшным, потому что компилятор звучит как очень продвинутая тема информатики для кого -то вроде меня (у меня нет степени CS).

Тем не менее, нам повезло, потому что компилятор TypeScript на самом деле является «очень простым» компилятором, потому что ему не нужно иметь дело с такими вещами, как аппаратная архитектура или управление памятью (время выполнения JavaScript заботится об этих вещах). Если вы всегда хотели узнать, как работает компилятор, внесение вкладчиков в TypeScript на самом деле очень дружелюбный способ сделать это.

Я лично начал узнавать о компиляторе, наблюдая за многими Андерс Хейлсберг Интервью в Интернете. Он всегда рассказывает о таких вещах, как «переписывание эмиттера TypeScript как излучателя преобразования на основе дерева». Сначала я не получил все, что он сказал, но прослушивание его интервью за эти годы привело меня к тому, чтобы получить некоторые базовые знания об архитектуре компилятора TypeScript.

Я смотрел много видео и прочитал много документов. Я не помню их всех, но это те, которые застряли в моей памяти:

Около года назад я сделал Небольшой эксперимент в хакатоне на глобальном саммите Microsoft MVP в Редмонде. Я попытался создать расширение для преобразования кода TypeScript в диаграмму UML.

Я никогда полностью не закончил эксперимент Но это был мой первый прямой контакт с инструментами AST TypeScript и языковой службы. Я нашел этот небольшой эксперимент очень полезным, и я бы порекомендовал играть с языковыми услугами в качестве учебного упражнения.

После того, как вам удастся понять различные этапы процесса компиляции и то, что делает каждый из них, вы должны быть готовы начать пытаться выбрать проблему.

Как я могу найти чем заняться?

Менеджеры проектов TypeScript создали веху для Проблемы, которые рекомендуются для сообщества Анкет Некоторые из этих вопросов помечены как “Хороший первый выпуск” Анкет Вы должны попытаться пройти через эти проблемы и найти тот, который вы можете понять.

Что я могу сделать, если все кажется слишком сложным?

Я много раз посещал веху сообщества в течение многих месяцев, и я много раз оставлял его разочарованным, потому что я не чувствовал, что смог помочь. Я продолжал учиться и посещать эту страницу, пока однажды я не увидел проблему, которая казалась чем -то, что я мог сделать.

После вашего первого PR ваш уровень уверенности будет расти, и пройдет недолго, пока вы не найдете следующую возможность для нового PR.

О выпуске № 20026

Проблема, которую я выбрал для моего самого первого вклада, была номером выпуска #20026 Анкет

В этом конкретном вопросе кто -то предположил, что когда мы пытаемся вызвать нулевой или неопределенное переменная:

null()

Мы получаем Объект, возможно, «null» ошибка. Эта ошибка не очень удобна для пользователя, и вместо этого использовалась одна из следующих ошибок:

Cannot invoke an object which is possibly 'null'.
Cannot invoke an object which is possibly 'undefined'.
Cannot invoke an object which is possibly 'null' or 'undefined'.

Я смог понять требование проблемы, а также подумал, что смогу найти, где ошибка Объект, возможно, «null» брошен и изменяет его для одной из других ошибок, когда выражение является функция вызов.

Впервые я нашел проблему, которая звучала не слишком сложной, поэтому я решил попробовать.

Как я могу внести свой вклад?

Найдя вопрос, представляющий наш интерес, мы можем начать работать над этим. Нам нужно пройти следующие шаги:

  1. Настройка проекта
  2. Внедрение и тестирование ваших изменений
  3. Отправка PR

1. Развитие проекта

  • Создайте вилку проекта TypeScript.

    Пожалуйста, обратитесь к Документация GitHub Если вам нужна дополнительная помощь о том, как разобраться в хранилище.

  • Клонировать свою вилку

git clone https://github.com/YOUR_GITHUB_USER_NAME_GOES_HERE/TypeScript.git

Пожалуйста, обратитесь к Документация GitHub Если вам нужна дополнительная помощь о том, как клонировать репозиторий.

  • Установите Джейк (требуется node.js )
npm install -g jake
  • Установите проект зависимости
npm install
  • Запустите тесты
jake runtests-parallel

Если все тесты сработали успешно, вы должны быть готовы начать работу над своим вкладом.

Я рекомендую вам поработать над новым филиалом. В моем случае создал филиал с именем вопроса, над которой я работал:

git checkout -b issue-20026

2. Внедрение и тестирование ваших изменений

Наш первый PR, скорее всего, станет исправлением ошибки, а не новой функцией. Лучший способ исправить ошибку – начать с написания теста, который воспроизводит ошибку.

Поэтому я начал с попытки найти существующие тесты. Я открыл Тесты папка Но я не смог найти что -то, что было похоже на модульный тест.

Тесты TypeScript немного странные, потому что они используют мокко очень абстрактным образом. Нам не нужно писать тестовые приспособления или тестирование случаев, вместо этого мы пишем фрагмент кода типографии, который, как ожидается, будет работать определенным образом или бросить определенную ошибку компиляции. Затем инструменты тестирования будут генерировать некоторые выходы, и для каждого из этих выходов будет создан тестовый пример.

Чтобы написать тест, нам нужно создать новый файл в папке Tests ( /tests/case/compiler/ ) с уникальным именем. Anplying.md Файл дает несколько советов о имени уникальности:

Обратите внимание, что имена файлов здесь должны отличаться от всех других имен тестирования компилятора, поэтому вам, возможно, придется немного работать, чтобы найти уникальное имя, если это что -то общее.

Файл должен содержать код TypeScript, который вы хотите проверить. В моем случае я создал файл с именем NullablefunctionEerror.ts Анкет

/tests/cases/compiler/nullablefunctionerror.ts

Мой NullablefunctionEerror.ts содержит следующий код типографии:

// @strictNullChecks: true

null();
undefined();
let f: null | undefined;
f();

В предыдущем фрагменте кода используется три вызова функции: нулевой(); , неопределенный(); и f (); Анкет Каждый из этих вызовов должен вызвать каждую из новых ошибок, ожидаемых выпуском № 20026.

Как вы уже заметили, фрагмент кода не содержит никаких утверждений. Проект TypeScript использует предыдущий вывод компиляции в качестве утверждения тестов.

Мы можем выполнить тест, используя следующую команду для выполнения теста:

jake runtests tests=nullableFunctionError

Компилятор TypeScript затем генерирует следующие файлы в качестве вывода:

  • NullableFunctionEerror.Errors.txt
  • NullablefunctionError.js
  • NullableFunctionError.SyMbols
  • NullableFunctionError.Types

Эти файлы хранятся в управлении источником в рамках /Тесты/Базовые линии/Ссылка/ каталог. Когда тесты выполняются, файлы повторно генерируются в рамках /Тесты/Базовые линии/локальные/ каталог. Затем файлы в обоих каталогах сравниваются, чтобы проверить, изменилось ли поведение компилятора.

Вы можете использовать следующую команду для сравнения двух версий:

jake diff

А также Следующая команда принять изменения:

jake baseline-accept

Поскольку это новый тест, не существует предыдущих версий файлов, и нам нужно принять новые файлы, используя Jake Baseline-Accept Анкет

Не волнуйтесь слишком много об использовании Jake Baseline-Accept По ошибке, потому что вы сможете отказываться от изменений, используя git, если вам нужно это сделать.

В моем случае NullableFunctionEerror.Errors.txt содержал следующий контент:

tests/cases/compiler/nullableFunctionError.ts(1,1): error TS2531: Object is possibly 'null'.
tests/cases/compiler/nullableFunctionError.ts(2,1): error TS2531: Object is possibly 'null'.
tests/cases/compiler/nullableFunctionError.ts(4,1): error TS2531: Object is possibly 'null'.


==== tests/cases/compiler/nullableFunctionError.ts (3 errors) ====
    null();
    ~~~~
!!! error TS2721: Object is possibly 'null'.
    undefined();
    ~~~~~~~~~
!!! error TS2722: Object is possibly 'null'.
    let f: null | undefined;
    f();
    ~
!!! error TS2723: Object is possibly 'null'.

Как мы видим, три ошибки Объект, возможно, «нуль». Но они должны быть:

Cannot invoke an object which is possibly 'null'.
Cannot invoke an object which is possibly 'undefined'.
Cannot invoke an object which is possibly 'null' or 'undefined'.

Это было правильно, потому что я ничего не изменил в компиляторе TypeScript. На этом этапе мне нужно было выяснить, что нужно изменить, чтобы отображаться правильные ошибки.

У меня уже был тест на месте, и я мог бы узнать, были ли мои изменения правильными, проверив содержимое NullableFunctionEerror.Errors.txt файл. Кроме того, уже было 58656 Существующие тесты Это даст мне знать, если я поменю что -то еще по ошибке. Это очень очевидный пример преимуществ Tdd Анкет

/src/compiler/diagnosticmessages.json

Первое, что я попытался сделать, это выяснить, откуда исходило текущее сообщение об ошибке. В итоге я добавил три новых ошибки в файл с именем DiagnosticMessage.json :

"Cannot invoke an object which is possibly 'null'.": {
    "category": "Error",
    "code": 2721
},
"Cannot invoke an object which is possibly 'undefined'.": {
    "category": "Error",
    "code": 2722
},
"Cannot invoke an object which is possibly 'null' or 'undefined'.": {
    "category": "Error",
    "code": 2723
},

/src/compiler/checker.ts

Следующим шагом было бросить новые три ошибки, которые я создал в DiagnosticMessage.json файл.

Этот шаг был интенсивным учебным упражнением, потому что у меня было очень мало знаний о домене о компиляторе TypeScript. Мой единственный вариант состояла в том, чтобы попытаться получить некоторые знания с помощью проб, ошибок и экспериментов.

Мне удалось выяснить, что я могу запустить все тесты, используя следующую команду:

jake runtests-parallel

Я также мог бы запустить только свой тест, используя следующую команду:

jake runtests tests=nullableFunctionError

Я также мог бы отладить свои тесты, используя следующую команду и инструменты отладки Chrome:

jake runtests-browser tests=nullableFunctionError browser=chrome

Я нашел всю эту информацию в Anplying.md файл.

Поскольку ошибки были ошибками типа, я смог догадаться, что я должен внести некоторые изменения в шахте.

Еще раз я начал с поиска, были TS2723: Объект, возможно, «null» Ошибка использовалась в контролере типа. Я закончил тем, что посмотрел на checknonnulltype и CheckNonnullexpression функции.

Три новые ошибки актуальны только для вызовов функций, но функция checknonnulltype использовался во многих случаях, не только для функциональных вызовов.

Через некоторое время экспериментировал, я выяснил, что мне нужно передать три новых ошибки в качестве дополнительных аргументов, чтобы CheckNonnullexpression и передать их checknonnulltype :

function checkNonNullExpression(
    node: Expression | QualifiedName,
    nullDiagnostic?: DiagnosticMessage,
    undefinedDiagnostic?: DiagnosticMessage,
    nullOrUndefinedDiagnostic?: DiagnosticMessage,
) {
    return checkNonNullType(
        checkExpression(node),
        node,
        nullDiagnostic,
        undefinedDiagnostic,
        nullOrUndefinedDiagnostic
    );
}

checknonnulltype Затем также примут три новых ошибки в качестве необязательных аргументов и использовать их, когда это необходимо:

function checkNonNullType(
    type: Type,
    node: Node,
    nullDiagnostic?: DiagnosticMessage,
    undefinedDiagnostic?: DiagnosticMessage,
    nullOrUndefinedDiagnostic?: DiagnosticMessage
): Type {
    const kind = (strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable;
    if (kind) {
        error(node, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ?
            (nullOrUndefinedDiagnostic || Diagnostics.Object_is_possibly_null_or_undefined) :
            (undefinedDiagnostic || Diagnostics.Object_is_possibly_undefined) :
            (nullDiagnostic || Diagnostics.Object_is_possibly_null)
        );
        const t = getNonNullableType(type);
        return t.flags & (TypeFlags.Nullable | TypeFlags.Never) ? unknownType : t;
    }
    return type;
}

Последнее изменение состояло в том, чтобы предоставить три новых ошибки в качестве аргументов CheckNonnullexpression Когда был использован функциональный вызов. Я пытался искать такие вещи, как вызвать или Позвоните В исходном коде мне удалось выяснить что Resolvecallexpression Функция была тем, что я искал.

function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[]): Signature {
    // ...

    const funcType = checkNonNullExpression(
        node.expression,
        Diagnostics.Cannot_invoke_an_object_which_is_possibly_null,
        Diagnostics.Cannot_invoke_an_object_which_is_possibly_undefined,
        Diagnostics.Cannot_invoke_an_object_which_is_possibly_null_or_undefined
    );
    // ...

Я выполнил тесты и обнаружил неожиданные результаты, потому что мои тесты не выполнялись с использованием не нулевых типов. Я понял это благодаря отладчику Chrome. Код, который заставляет меня определить проблему, можно найти в checknonnulltype Функция:

const kind = (strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable;

Я нашел, как включить не нулевые файлы в Anplying.md файл:

Эти файлы поддерживают теги метаданных в формате//@metadataname: значение. Поддерживаемые имена и значения такие же, как и те, которые поддерживаются в самом компиляторе.

Решением было добавить флаг //@strictnullchecks: true в тестовом файле NullableFunctionError.ts . Я выполнил тесты еще раз, и следующие файлы были сгенерированы, как и ожидалось.

/tests/cases/compiler/nullablefunctionerror.errors.txt

Содержит список ошибок, обнаруженных компилятором. На этот раз ошибки были правильными:

tests/cases/compiler/nullableFunctionError.ts(1,1): error TS2721: Cannot invoke an object which is possibly 'null'.
tests/cases/compiler/nullableFunctionError.ts(2,1): error TS2722: Cannot invoke an object which is possibly 'undefined'.
tests/cases/compiler/nullableFunctionError.ts(4,1): error TS2723: Cannot invoke an object which is possibly 'null' or 'undefined'.


==== tests/cases/compiler/nullableFunctionError.ts (3 errors) ====
    null();
    ~~~~
!!! error TS2721: Cannot invoke an object which is possibly 'null'.
    undefined();
    ~~~~~~~~~
!!! error TS2722: Cannot invoke an object which is possibly 'undefined'.
    let f: null | undefined;
    f();
    ~
!!! error TS2723: Cannot invoke an object which is possibly 'null' or 'undefined'.

/tests/cases/compiler/nullablefunctionerror.js

Содержит вход (TypeScript) и код Output (JavaScript):

//// [nullableFunctionError.ts]
null();
undefined();
let f: null | undefined;
f();


//// [nullableFunctionError.js]
null();
undefined();
var f;
f();

/tests/cases/compiler/nullablefunctionerror.symbols

Содержит список символов, созданных компилятором:

=== tests/cases/compiler/nullableFunctionError.ts ===
null();
undefined();
>undefined : Symbol(undefined)

let f: null | undefined;
>f : Symbol(f, Decl(nullableFunctionError.ts, 2, 3))

f();
>f : Symbol(f, Decl(nullableFunctionError.ts, 2, 3))

/tests/cases/compiler/nullablefunctionerror.types

Содержит список типов, обнаруженных компилятором:

=== tests/cases/compiler/nullableFunctionError.ts ===
null();
>null() : any
>null : null

undefined();
>undefined() : any
>undefined : undefined

let f: null | undefined;
>f : null | undefined
>null : null

f();
>f() : any
>f : null | undefined

3. Отправка PR

В этот момент я был почти готов закончить свой PR. Я принял новые базовые файлы:

jake baseline-accept

И я выполнил весь существующий тест:

jake runtests-parallel

Если ваши тесты пройдут локально, вполне вероятно, что у вас не будет никаких проблем в сборке CI.

Если у вас есть какие -либо проблемы, команда TypeScript должна иметь возможность помочь вам, не бойтесь обращаться за помощью!

Пожалуйста, обратитесь к Документация GitHub Если вам нужна дополнительная помощь о том, как создать PR.

Подписание CLA

Проекты TypeScript требуют, чтобы участники подписывали лицензионное соглашение о взносе (CLA).

Anplying.md Файл содержит некоторые рекомендации по этому поводу:

Вам нужно будет завершить лицензионное соглашение о вклад (CLA). Вкратце, настоящее Соглашение свидетельствует о том, что вы предоставляете нам разрешение на использование представленных изменений в соответствии с условиями лицензии проекта и что представленная работа находится под соответствующим авторским правом. Пожалуйста, отправьте лицензионное соглашение о внесении лицензии (CLA), прежде чем отправить запрос на привлечение. Вы можете посетить https://cla.microsoft.com подписать в цифровом виде.

Резюме

В этой статье мы узнали, как мы можем внести свой вклад в TypeScript на GitHub через реальный пример.

Я надеюсь, что вам понравился этот пост, и он поможет вам отправить ваш первый пиар в проект TypeScript.

Счастливого кодирования!

Оригинал: “https://dev.to/remojansen/learn-how-to-contribute-to-the-typescript-compiler-on-github-through-a-real-world-example-4df0”