Как вы явно устанавливаете новое свойство для `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который определенно может содержать что угодно.


Ответы:


693

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

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

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

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


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

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 и новее!


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 || {};

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;

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



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 {} 

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

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


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

2

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

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

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.