Модуль Typescript на момент JS ведет себя странно

Я пытаюсь использовать momentJs из машинописного текста: в зависимости от того, какую модульную систему я использую для компиляции машинописного текста, я нахожу другое поведение относительно того, как я могу использовать momentJ. При компиляции машинописи с помощью commonJs все работает как положено, и я могу просто следовать документации момента JJ:

import moment = require("moment");
moment(new Date()); //this works

Если я использую "систему" ​​в качестве системного модуля, когда я импортирую "момент", я вынужден сделать

import moment = require("moment");
moment.default(new Date()); //this works
moment(new Date()); //this doesn't work

Я нашел обходной путь, чтобы заставить их работать, независимо от используемой системы модулей машинописи

import m = require("moment")
var moment : moment.MomentStatic;
moment = (m as any).default || m;

Мне это не нравится, и я хотел бы понять, почему он так себя ведет. Я делаю что-то неправильно? Кто-нибудь может объяснить мне, что происходит?

9 ответов

Решение

Это происходит потому, что SystemJS автоматически конвертирует moment к модулю в стиле ES6, обернув его в объект модуля, а CommonJS - нет.

Когда CommonJS подъезжает momentмы получаем фактическое moment функция. Это то, что мы делаем в JavaScript некоторое время, и это должно выглядеть очень знакомо. Это как если бы вы написали:

var moment = function moment() {/*implementation*/}

Когда SystemJS тянет moment, это не дает вам функцию момента. Создает объект с функцией момента, назначенной свойству с именем default, Это как если бы вы написали:

var moment = {
    default: function moment() {/*implementation*/}
}

Почему это так? Поскольку модуль должен быть картой одного или нескольких свойств, а не функцией, согласно ES6/TS. В ES6 условием для массовых внешних библиотек, которые ранее экспортировались сами, является экспорт в соответствии с default свойство объекта модуля, использующего export default (подробнее здесь; в ES6/TypeScript вы можете импортировать такие функции, используя компактный import moment from "moment" синтаксис).

Вы не делаете ничего плохого, вам просто нужно выбрать формат ваших импортированных модулей и придерживаться вашего выбора. Если вы хотите использовать как CommonJS, так и SystemJS, вы можете настроить их для использования одного и того же стиля импорта. Поиск 'systemjs default import' привел меня к обсуждению вашей проблемы, в котором они реализуют --allowSyntheticDefaultImports установка.

Я решил проблему, заменив

      import moment from "moment";

оператор импорта с

      import * as moment from "moment";

это.

Я сделал следующее:

Я установил moment определение файла следующим образом:

tsd install moment --save

Затем я создал main.ts:

///<reference path="typings/moment/moment.d.ts" />

import moment = require("moment");
moment(new Date());

И я побежал:

$ tsc --module system --target es5 main.ts # no error 
$ tsc --module commonjs --target es5 main.ts # no error 

main.js выглядит так:

// https://github.com/ModuleLoader/es6-module-loader/blob/v0.17.0/docs/system-register.md - this is the corresponding doc
///<reference path="typings/moment/moment.d.ts" />
System.register(["moment"], function(exports_1) {
    var moment;
    return {
        setters:[
            function (moment_1) {
                // You can place `debugger;` command to debug the issue
                // "PLACE XY"
                moment = moment_1;
            }],
        execute: function() {
            moment(new Date());
        }
    }
});

Моя версия TypeScript 1.6.2.

Вот что я узнал:

Momentjs экспортирует функцию (т.е. _moment = utils_hooks__hooks а также utils_hooks__hooks это функция, это вполне понятно.

Если вы установите точку останова в месте, которое я обозначил как PLACE XY выше, вы можете видеть, что moment_1 это объект (!), а не функция. Соответствующие строки: 1, 2

TL; DR: В заключение, проблема не имеет ничего общего с TypeScript. Проблема в том, что systemjs не сохраняет информацию о том, что моментальный файл экспортирует функцию. Systemjs просто копирует свойства экспортируемого объекта из модуля (функция также является объектом в JavaScript). Я полагаю, вам следует зарегистрировать проблему в репозитории systemjs, чтобы узнать, считают ли они, что это ошибка (или фича:)).

Вот как я это сделал с System.js и Typescript 1.7.5

import * as moment from "moment";
...
moment.utc(); // outputs 2015 (for now).

Но обратите внимание, что я использую utc() метод. Я не могу использовать moment() потому что как тк. объяснил, это превращается в moment.default() System.js. Причины Типизированных Типизированных Типов не содержат default метод, поэтому, чтобы избежать ошибки компиляции, нужно использовать что-то вроде moment["default"]() (Знаю, некрасиво).

Следующим шагом мне нужно было добавить следующее в конфигурацию System.js:

System.config({
  paths: {
    'moment': 'node_modules/moment/moment.js'
  }
});

После этого все заработало как шарм.

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

import momentRef = require('moment');
var moment: moment.MomentStatic = momentRef;

Если вы используете машинописный текст, импорт по умолчанию, например import moment from "moment" действует так же, как const moment = require("moment").default. См. Https://www.typescriptlang.org/tsconfig#esModuleInterop

TL; DR; Добавить esModuleInterop: true в вашем tsconfig.json

Я имел

import * as moment from 'moment';

и изменил все, что я думал, должно быть

var date: moment = moment();

в

var date: moment.Moment = moment();

Для моего system.config:

paths: {
    'moment': 'node_modules/moment/moment.js'
},
packages: {
    app: {
        format: 'register',
        defaultExtension: 'js'
    }
}

Импортируя моменты в моем компоненте, я удалил *, который, я думаю, обрабатывает код в файле moment.js как несколько объектов.

Изменить:

import * as moment from 'moment';

чтобы:

import moment from 'moment';

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

По принципу, что более понятный код — лучший код, вы должны явно импортировать то, что хотите использовать в модуле. Использование import * импортирует все в модуль и рискует запутать сопровождающих. Аналогично, экспортируйте * из "модуля"; импортирует, а затем реэкспортирует все в модуле и рискует запутать не только сопровождающих, но и пользователей модуля.

Другие вопросы по тегам