Як ви явно встановите нове властивість на `window` у TypeScript?


619

Я встановлюю глобальні простори імен для своїх об'єктів, явно встановлюючи властивість window.

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

TypeScript підкреслює MyNamespaceта скаржиться, що:

Властивість "MyNamespace" не існує у значенні типу "window" any "

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

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

Як я можу втриматися windowтам і зробити щасливим TypeScript?

Як бічну зауваження, мені здається, що особливо смішно, що TypeScript скаржиться, оскільки він говорить мені, що windowтакого типу, anyякий, безумовно, може містити що завгодно.


Відповіді:


690

Щойно знайшов відповідь на це в іншій відповіді на питання StackOverflow .

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

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

В основному вам потрібно розширити існуючий windowінтерфейс, щоб розповісти про свою нову властивість.


11
Зверніть увагу на велику величину W у вікні. Це мене спалохало.
айм

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.коли я помістив його у верхній частині файлу, він виправив цю помилку, тож вам, можливо, доведеться це зробити і.
Зельфір Кальтшталь

397

Щоб зберегти його динамічно, просто використовуйте:

(<any>window).MyNamespace

9
Будь-які ідеї, як це зробити у файлі tsx?
velop

2
@martin: <any> робить це явним, тому я вважаю, що це працює чудово. Інші: Якщо ви використовуєте Typescript з React або іншим середовищем JSX (в результаті чого синтаксис TSX), вам доведеться використовувати asзамість <>. Дивіться відповідь @ david-boyd нижче .
Дон

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 також використовує глобальні. Однак якийсь застарілий код вимагає цього.
nathancahill

3
@Ramesh window['MyNamespace']()(просто додайте дужки)
iBaff

6
"Це повністю летить перед сильним набором тексту, вся ідея за TypeScript." ДОБРО!
Коді

188

Використовуєте TSX? Жодна з інших відповідей не працювала для мене.

Ось що я зробив:

(window as any).MyNamespace

4
Те саме, що (<any> window).MyNamespaceнасправді
Дмитро Паржицький

44
Це те саме, за винятком використання TSX, оскільки <any>інтерпретується як JSX, а не типовий формат.
Джейк Буун

1
Дякуємо @David, це спрацювало як шарм із новою версією Create React App та typecript версією, раніше це працювало: (<any> вікно).
Jignesh Raval

вікно, як будь-яке? Тоді для чого ти будеш використовувати машинопис? Просто використовуйте 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()
Martin Majewski

Це дуже приємна відповідь, якщо ви не хочете декларувати щось глобальним. З іншого боку, вам потрібно зателефонувати declare var...в кожен потрібний файл.
Пуче

Плюс один для цього підходу, оскільки в цьому випадку ви не конфліктуєте з іншими пакетами, які розширюють глобальне вікно в монорепо.
Дмитро Сорін

Дякую! Найкраща відповідь ІМО. Не слід перекреслювати 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 Object і подивіться, що станеться, коли ви робите нову String) - Зіткнення імен, що спричиняє побічний ефект на коді .. (особливо на JavaScript, який має асинхронний характер) Я можу продовжувати, але я думаю, ви можете зобразити зображення відпочинок ...
onalbi

1
@onabi Я буквально кажу про особливості браузера. Він доступний у всьому світі, оскільки це браузер. Ви дійсно припускаєте, що все-таки неправильно шукати глобальні визначення? Я маю на увазі, що ми могли б реалізувати Ponyfills, але це роздуває браузери, які фактично підтримують цю функцію (наприклад, повноекранний api). Це сказало, що я не вірю, що всі глобалі злі переслідують.
Mathijs Segers

2
@MathijsSegers, на мій погляд, глобальна змінна повинна уникати використання або модифікації там, де ми можемо .. це правда, що вікно доступне з тих пір, як браузер існує, але це правда, яка постійно мутувала .. так, якщо, наприклад, ми визначаємо зараз window.feature = 'Особливість'; і це використовується масово за кодом .. що трапиться, якщо браузер додасть window.feature по всьому коду, ця функція буде переопрацьована .. все одно я дав пояснення, що моє речення не йде проти вас .. З повагою ...
оналбі

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

Для тих, хто використовує кутовий CLI, це просто:

src / polyfills.ts

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

my-custom-utils.ts

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

Якщо ви використовуєте IntelliJ, вам також потрібно було змінити наступне налаштування в IDE, перш ніж підбирати нові поліфіли:

> File 
> Settings 
> Languages & Frameworks 
> TypeScript 
> check 'Use TypeScript Service'.

1
declare globalце хитрість, і ця відповідь насправді не специфічна для кутового CLI ...
Авіндра Голчаран

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/isissue/3180#issuecomment-102523512

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

Приклад наведено нижче:

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

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

Тепер код вище об'єднує типізацію простору імен MyNamespaceта інтерфейс MyNamespaceу глобальну змінну myNamespace(властивість windows ).


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 року) та архівувалась власником.
mrm

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, який використовує користувальницький інтерфейс, але оцінюється з оригінальним вікном.

Він працює з typecript@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-react-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.