Как вы явно устанавливаете новое свойство для `window` в TypeScript?

622

Я устанавливаю глобальные пространства имен для своих объектов, явно устанавливая свойство в window.

window.MyNamespace = window.MyNamespace || {};

TypeScript подчеркивает MyNamespaceи жалуется, что:

Свойство 'MyNamespace' не существует для значения типа 'window' any "

Я могу заставить код работать, объявив MyNamespaceпеременную окружения и отбросив windowявность, но я не хочу этого делать.

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

Как я могу остаться windowтам и сделать TypeScript счастливым?

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

joshuapoehls
источник
см. ссылку
MHS

Ответы:

693

Только что нашел ответ на этот вопрос в ответе на другой вопрос StackOverflow .

declare global {
    interface Window { MyNamespace: any; }
}

window.MyNamespace = window.MyNamespace || {};

По сути, вам нужно расширить существующий windowинтерфейс, чтобы рассказать о своем новом свойстве.

joshuapoehls
источник
12
Обратите внимание на заглавную букву W в окне. Это сбило меня с толку.
13
2
Я не мог получить это для компиляции с TSC 1.0.1.0. Однако ответ Блейка Митчелла сработал для меня.
Пэт
43
Имейте в виду, что это работает только тогда, когда объявлено в отдельном .d.tsфайле. Он не работает при использовании в .tsфайле, который использует импорт и экспорт сам. Смотрите этот ответ .
cdauth
36
В declare global { interface Window { ... } }работе с машинописью 2.5.2, нет необходимости в .d.tsфайл не как упоминалось выше
tanguy_k
2
Сначала машинопись сказала мне: error TS1234: An ambient module declaration is only allowed at the top level in a file.когда я поместил его в начало файла, он исправил эту ошибку, так что, возможно, вам придется это сделать.
Зельфир Кальцталь
398

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

(<any>window).MyNamespace
chinupson
источник
9
Любые идеи, как это сделать в TSX-файл?
ворачиваются
2
@martin: <any> делает это явным, поэтому я думаю, что он работает отлично. Другие: Если вы используете Typescript с React или другой средой JSX (что приводит к синтаксису TSX), вам придется использовать asвместо <>. Смотрите ответ @ Дэвид-Бойд ниже .
Дон
31
Я должен был использовать (окно, как любое). MyNamespace
Арсалан Ахмад
Точно так же для this-return (<any> this)['something in scope']
HankCa
1
Мне пришлось отключить tslint на этой линии с/* tslint:disable */
Михаил Ягудаев
197

КАК ТИПИСКРИПТ ^ 3.4.3 НАСТОЯЩЕГО РЕШЕНИЯ НЕ ДЕЙСТВУЕТ

Или...

Вы можете просто набрать:

window['MyNamespace']

и вы не получите ошибку компиляции, и она работает так же, как печатать window.MyNamespace

Эван Ларсен
источник
15
но вы, вероятно, получите ошибку tslint ... Если у вас есть, конечно,
smnbbrv
69
Это полностью противоречит строгой типизации, и в этом вся идея TypeScript.
d512
18
@ user1334007 также использует глобальные переменные. Однако, некоторый унаследованный код требует этого.
Натанкахилл
3
@ Рамеш window['MyNamespace']()(просто добавьте скобки)
iBaff
6
«Это полностью противоречит строгой типизации, всей идее, лежащей в основе TypeScript». ХОРОШО!
Коди
188

Используете TSX? Ни один из других ответов не работал для меня.

Вот что я сделал:

(window as any).MyNamespace
Дэвид Бойд
источник
4
Так же, как на (<any> window).MyNamespaceсамом деле
Дмитрий Паржицкий
44
То же самое, за исключением случаев использования TSX, поскольку <any>интерпретируется как JSX, а не как приведение типа.
Джейк Бун
1
Благодаря @David, это работало как чудо с новым приложением Create React и версией машинописи. Раньше это работало: (<any> окно) .MyNamespace, но теперь оно ломается в новой версии машинописи 3.5.x.
Джинеш Равал
окно как нибудь? Тогда почему вы используете машинопись? Просто используйте Javascript x)
MarcoLe
71

Принятый ответ - это то, что я использовал, но с TypeScript 0.9. * Он больше не работает. Новое определение Windowинтерфейса, похоже, полностью заменяет встроенное определение, а не расширяет его.

Я взялся за это вместо этого:

interface MyWindow extends Window {
    myFunction(): void;
}

declare var window: MyWindow;

ОБНОВЛЕНИЕ: С TypeScript 0.9.5 принятый ответ снова работает.

Блейк Митчелл
источник
2
Это работает также с модулями, используемыми в TypeScript 2.0.8. Пример: export default class MyClass{ foo(){ ... } ... } interface MyWindow extends Window{ mc: MyClass } declare var window: MyWindow window.mc = new MyClass() После этого вы можете вызвать Foo () , например , из Chrome Dev Tools консоли , как mc.foo()
Мартин Маевский
Это очень хороший ответ, если вы не хотите объявлять что-то глобальным. С другой стороны, вам нужно вызывать declare var...каждый файл, который вам нужен.
Puce
Плюс один для этого подхода, потому что в этом случае вы не конфликтуете с другими пакетами, которые расширяют глобальное окно в monorepo.
Дмитрий Сорин
Спасибо! Лучший ответ ИМО. Не следует отвергать Windowтот простой факт, что это не ванильное окно. Просто обходить проверку типов или пытаться обмануть TS также не способ сделать это.
Рутгер Виллемс
Это не работает с TS 3.6, к сожалению
Джейкоб Содерлунд
65

Глобальные являются «злом» :), я думаю, что лучший способ иметь также переносимость:

Сначала вы экспортируете интерфейс: (например: ./custom.window.ts)

export interface CustomWindow extends Window {
    customAttribute: any;
}

Второй вы импортируете

import {CustomWindow} from './custom.window.ts';

Третье приведенное глобальное окно var с CustomWindow

declare let window: CustomWindow;

Таким образом, у вас также не будет красной линии в другой IDE, если вы используете существующие атрибуты объекта окна, поэтому в конце попробуйте:

window.customAttribute = 'works';
window.location.href = '/works';

Протестировано с Typescript 2.4.x и новее!

onalbi
источник
1
Возможно, вы захотите рассказать, почему глобалы злые. В нашем случае мы используем нестандартизированный API для оконного объекта. На данный момент она заполнена. Это действительно зло?
Mathijs Segers
1
@MathijsSegers Я не знаю, особенно ваш случай, но .. о глобальных зла зла не только о JavaScript, но на каждом языке .. некоторые причины: - Вы не можете применить шаблон проектирования хорошо - Проблемы с памятью и производительностью (Вы иметь их везде, попытаться прикрепить что-то к объекту String и посмотреть, что произойдет, когда вы создадите новую строку) - столкновение имен, вызывающее побочный эффект для кода .. (особенно для javascript, который имеет асинхронную природу), я могу продолжить, но я думаю, что вы можете изобразить отдых ...
onalbi
1
@onabi Я буквально говорю о функции браузера. Это доступно во всем мире, так как это браузер. Вы действительно предположили бы, что все еще неправильно идти к глобальным определениям? Я имею в виду, что мы могли бы реализовать Ponyfills, но это раздуло бы браузеры, которые фактически поддерживают эту функцию (например, полноэкранный API). Тем не менее, я не верю, что все глобалы злые.
Mathijs Segers
2
@MathijsSegers, по моему мнению, следует избегать использования или изменения глобальной переменной там, где мы можем .. верно, что окно доступно, поскольку браузер существует, но также верно и то, что оно постоянно изменялось, поэтому, если, например, мы сейчас определяем window.feature = 'Feature'; и это широко используется в коде ... что произойдет, если window.feature добавлен браузерами во весь код, эта функция переопределена ... в любом случае, я дал объяснение моего предложения не идти против вас ... с уважением ...
Onalbi
48

Если вам нужно расширить windowобъект пользовательским типом, который требует его использования, importвы можете использовать следующий метод:

window.d.ts

import MyInterface from './MyInterface';

declare global {
    interface Window {
        propName: MyInterface
    }
}

См. «Глобальное увеличение» в разделе «Слияние деклараций» Руководства: https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation.

Люцифер Сэм
источник
43

Для тех, кто использует Angular CLI, это просто:

SRC / polyfills.ts

declare global {
  interface Window {
    myCustomFn: () => void;
  }
}

My-таможенно-utils.ts

window.myCustomFn = function () {
  ...
};

Если вы используете IntelliJ, вам также необходимо изменить следующие настройки в IDE, прежде чем ваши новые полифиллы будут загружены:

> File 
> Settings 
> Languages & Frameworks 
> TypeScript 
> check 'Use TypeScript Service'.
Стивен Пол
источник
1
declare globalэто хитрость, и этот ответ не совсем специфичен для Angular CLI ...
Avindra Goolcharan
31

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

Я просто сделал:

const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

Или вы могли бы сделать:

let myWindow = window as any;

а потом myWindow.myProp = 'my value';

Димитар Никовски
источник
1
В этом случае вы также можете установить пакет npm, который содержит все определения - как указано в документации
bbrinx
Вы можете сделать это, но это не ответ на вопрос, а скорее обходной путь
Виталий
Благодаря этому я мог правильно включить инструменты редукции в машинописном тексте, используя (окно как любое) .__ REDUX_DEVTOOLS_EXTENSION__ && (окно как любое) .__ REDUX_DEVTOOLS_EXTENSION __ ())
danivicario
20

Большинство других ответов не идеальны.

  • Некоторые из них просто подавляют вывод типа для магазина.
  • Некоторые другие заботятся только о глобальной переменной как о пространстве имен, но не как интерфейс / класс

Я также сталкиваюсь с подобной проблемой этим утром. Я пробовал так много «решений» на SO, но ни одно из них не выдает абсолютно никакой ошибки типа и не позволяет запускать переходы типа в IDE (webstorm или vscode).

Наконец, отсюда

https://github.com/Microsoft/TypeScript/issues/3180#issuecomment-102523512

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

Пример ниже:

// typings.d.ts
declare interface Window {
    myNamespace?: MyNamespace & typeof MyNamespace
}

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

Теперь вышеприведенный код объединяет типизацию пространства имен MyNamespaceи интерфейса MyNamespaceв глобальную переменную myNamespace(свойство window).

е-облако
источник
2
Спасибо - это единственное, что вы можете использовать в окружающем немодульном контексте.
Тайлер Себастьян
18

После поиска ответов, я думаю, что эта страница может быть полезной. https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation Не уверен насчет истории слияния объявлений, но он объясняет, почему может работать следующее.

declare global {
    interface Window { MyNamespace: any; }
}

window.MyNamespace = window.MyNamespace || {};
Sheng
источник
13

Вот как это сделать, если вы используете TypeScript Definition Manager !

npm install typings --global

Создать typings/custom/window.d.ts:

interface Window {
  MyNamespace: any;
}

declare var window: Window;

Установите свой пользовательский набор:

typings install file:typings/custom/window.d.ts --save --global

Готово, используй это ! Машинопись больше не будет жаловаться:

window.MyNamespace = window.MyNamespace || {};
Ник Сумейко
источник
1
FWIW typingsустарел с Typescript 2.0 (середина 2016 года) и был заархивирован владельцем.
Мрм
13

Typscript не выполняет проверку типов в свойствах строки.

window["newProperty"] = customObj;

В идеале сценарий глобальной переменной следует избегать. Я использую его иногда для отладки объекта в консоли браузера.

Irshad
источник
8

Если вы используете Typescript 3.x, вы можете пропустить declare globalчасть в других ответах и ​​вместо этого просто использовать:

interface Window {
  someValue: string
  another: boolean
}

Это работало со мной при использовании Typescript 3.3, WebPack и TSLint.

Дана Вудман
источник
Не работает в ^3.4.3. Этот ответ сработал для меня stackoverflow.com/a/42841166/1114926
Зеленый
7

Для справки (это правильный ответ):

Внутри .d.tsфайла определения

type MyGlobalFunctionType = (name: string) => void

Если вы работаете в браузере, вы добавляете элементы в контекст окна браузера, открывая интерфейс Window:

interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

Та же идея для NodeJS:

declare module NodeJS {
  interface Global {
    myGlobalFunction: MyGlobalFunctionType
  }
}

Теперь вы объявляете корневую переменную (которая на самом деле будет жить в окне или глобальной переменной)

declare const myGlobalFunction: MyGlobalFunctionType;

Затем в обычном .tsфайле, но импортированном как побочный эффект, вы фактически реализуете его:

global/* or window */.myGlobalFunction = function (name: string) {
  console.log("Hey !", name);
};

И, наконец, используйте его в другом месте кодовой базы:

global/* or window */.myGlobalFunction("Kevin");

myGlobalFunction("Kevin");
Бенуа Б.
источник
Спасибо, это лучший пример, который я нашел, и работает хорошо.
Ник Г.
6

Создание настраиваемого интерфейса расширяет окно и добавляет ваше настраиваемое свойство как необязательное.

Затем позвольте customWindow, использующему пользовательский интерфейс, но оцененному в исходном окне.

Это работает с typcript@3.1.3.

interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

customWindow.MyNamespace = customWindow.MyNamespace {} 
shuizhongyuemin
источник
5

Для тех, кто хочет установить вычисляемое или динамическое свойство windowобъекта, вы обнаружите, что это невозможно с помощью declare globalметода. Чтобы уточнить для этого случая использования

window[DynamicObject.key] // Element implicitly has an 'any' type because type Window has no index signature

Вы можете попытаться сделать что-то вроде этого

declare global {
  interface Window {
    [DyanmicObject.key]: string; // error RIP
  }
}

Выше будет ошибка, хотя. Это связано с тем, что в Typescript интерфейсы плохо работают с вычисляемыми свойствами и выдают ошибку вроде

A computed property name in an interface must directly refer to a built-in symbol

Чтобы обойти эту проблему , вы можете пойти с внушал кастинг windowна <any>так что вы можете сделать

(window as any)[DynamicObject.key]
август
источник
3
Это так 2019.
Qwerty
4

Используя create-Reaction-app v3.3, я обнаружил, что самый простой способ добиться этого - расширить Windowтип в сгенерированном автоматически react-app-env.d.ts:

interface Window {
    MyNamespace: any;
}
Крис Довер
источник
3

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

declare var window: any;

Это не переопределит объект окна или не создаст другую переменную с именем window.
Это означает, что окно определено где-то еще, и вы просто ссылаетесь на него в текущей области видимости.

Затем вы можете обратиться к своему объекту MyNamespace просто: -

window.MyNamespace

Или вы можете установить новое свойство windowобъекта просто:

window.MyNamespace = MyObject

И теперь машинопись не будет жаловаться.

Mav55
источник
Могу ли я узнать ваш случай использования, чтобы узнать, где windowопределен?
Mav55
2

Я хотел использовать это в библиотеке Angular (6) сегодня, и мне потребовалось некоторое время, чтобы заставить это работать как ожидалось.

Чтобы моя библиотека использовала объявления, мне пришлось использовать d.tsрасширение для файла, который объявляет новые свойства глобального объекта.

Итак, в конце концов, файл закончился чем-то вроде:

/path-to-angular-workspace/angular-workspace/projects/angular-library/src/globals.d.ts

После создания не забудьте выставить его в своем public_api.ts.

Это сделало это для меня. Надеюсь это поможет.

кортик
источник