Bagaimana Anda secara eksplisit mengatur properti baru di `window` di TypeScript?

621

Saya menyiapkan ruang nama global untuk objek saya dengan secara eksplisit menyetel properti window.

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

TypeScript menggarisbawahi MyNamespacedan mengeluh bahwa:

Properti 'MyNamespace' tidak ada pada nilai ketik 'jendela' apa pun "

Saya dapat membuat kode bekerja dengan mendeklarasikan MyNamespacesebagai variabel ambient dan menjatuhkan windowexplicitness tetapi saya tidak ingin melakukan itu.

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

Bagaimana saya bisa tetap windowdi sana dan membuat TypeScript bahagia?

Sebagai catatan saya merasa lucu bahwa TypeScript mengeluh karena memberitahu saya bahwa itu windowadalah tipe anyyang pasti dapat berisi apa saja.

joshuapoehl
sumber
lihat Tautan
MHS

Jawaban:

692

Baru saja menemukan jawabannya di jawaban pertanyaan StackOverflow lain .

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

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

Pada dasarnya Anda perlu memperluas windowantarmuka yang ada untuk memberi tahu tentang properti baru Anda.

joshuapoehl
sumber
12
Perhatikan huruf kapital W di Jendela. Itu membuat saya tersandung.
ajm
2
Saya tidak bisa mendapatkan ini untuk dikompilasi dengan tsc 1.0.1.0. Namun, jawaban Blake Mitchell berhasil bagi saya.
Pat
43
Ketahuilah bahwa ini hanya berfungsi ketika dideklarasikan dalam .d.tsfile terpisah . Tidak berfungsi saat digunakan dalam .tsfile yang menggunakan impor dan ekspor itu sendiri. Lihat jawaban ini .
cdauth
36
The declare global { interface Window { ... } }bekerja dengan naskah 2.5.2, tidak perlu untuk .d.tsberkas seperti yang disebutkan di atas
tanguy_k
2
Pada awalnya, naskah memberi tahu saya:, error TS1234: An ambient module declaration is only allowed at the top level in a file.ketika saya meletakkannya di bagian atas file, itu memang memperbaiki kesalahan itu, jadi Anda mungkin harus melakukannya juga.
Zelphir Kaltstahl
397

Untuk membuatnya dinamis, cukup gunakan:

(<any>window).MyNamespace
chinupson
sumber
9
Ada ide bagaimana melakukan ini dalam file tsx?
velop
2
@martin: <any> membuatnya eksplisit, jadi berfungsi dengan baik, saya percaya. Lainnya: Jika Anda menggunakan TypeScript dengan Bereaksi atau lingkungan JSX lainnya (menghasilkan sintaks TSX) Anda harus menggunakannya assebagai gantinya <>. Lihat jawaban @ david-boyd di bawah ini .
Don
31
Saya harus menggunakan (window as any) .MyNamespace
Arsalan Ahmad
Demikian pula untuk this-return (<any> this)['something in scope']
HankCa
1
Saya harus menonaktifkan tslint pada baris itu dengan/* tslint:disable */
Michael Yagudaev
197

SEBAGAI MACAM-MACAM KHUSUS ^ 3.4.3 SOLUSI INI TANPA KERJA YANG LEBIH LAMA

Atau...

Anda cukup mengetik:

window['MyNamespace']

dan Anda tidak akan mendapatkan kesalahan kompilasi dan berfungsi sama seperti mengetik window.MyNamespace

Evan Larsen
sumber
15
tetapi Anda mungkin akan mendapatkan kesalahan tslint ... Jika Anda memiliki satu saja
smnbbrv
69
Ini benar-benar terbang dalam menghadapi pengetikan yang kuat, seluruh ide di balik TypeScript.
d512
18
@ user1334007 menggunakan global juga. Namun, beberapa kode lawas mengharuskannya.
nathancahill
3
@ Ramesh window['MyNamespace']()(cukup tambahkan tanda kurung)
iBaff
6
"Ini sepenuhnya terbang dalam menghadapi pengetikan yang kuat, seluruh ide di balik TypeScript." BAIK!
Cody
188

Menggunakan TSX? Tidak ada jawaban lain yang bekerja untuk saya.

Inilah yang saya lakukan:

(window as any).MyNamespace
David Boyd
sumber
4
Sama seperti (<any> window).MyNamespacesebenarnya
Dmitry Parzhitsky
44
Itu sama kecuali ketika menggunakan TSX, karena <any>akan ditafsirkan sebagai JSX, bukan tipe pemain.
Jake Boone
1
Terima kasih @ David, ini bekerja seperti pesona dengan Create React App dan versi naskah yang baru, Sebelumnya ini berfungsi: (<any> jendela) .Namespace, tetapi sekarang melanggar dalam naskah jenis baru versi 3.5.x.
Jignesh Raval
jendela apa saja? Lalu mengapa Anda menggunakan naskah? Cukup gunakan Javascript x)
MarcoLe
71

Jawaban yang diterima adalah yang biasa saya gunakan, tetapi dengan TypeScript 0.9. * Tidak lagi berfungsi. Definisi baru dariWindow antarmuka tampaknya sepenuhnya menggantikan definisi built-in, bukan menambahnya.

Saya telah melakukan ini sebagai gantinya:

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

declare var window: MyWindow;

UPDATE: Dengan TypeScript 0.9.5 jawaban yang diterima berfungsi lagi.

Blake Mitchell
sumber
2
Ini berfungsi juga dengan modul seperti yang digunakan oleh TypeScript 2.0.8. Contoh: export default class MyClass{ foo(){ ... } ... } interface MyWindow extends Window{ mc: MyClass } declare var window: MyWindow window.mc = new MyClass() Kemudian Anda dapat memanggil foo () mis. Dari konsol Alat Dev Chrome seperti mc.foo()
Martin Majewski
Ini adalah jawaban yang sangat bagus jika Anda tidak ingin mendeklarasikan sesuatu sebagai global. Di sisi lain, Anda perlu memanggil declare var...setiap file yang Anda butuhkan.
Puce
Plus satu untuk pendekatan ini karena dalam hal ini Anda tidak bertentangan dengan paket lain yang memperpanjang jendela global di monorepo.
Dmitrii Sorin
Terima kasih! IMO jawaban terbaik. Seseorang seharusnya tidak mengesampingkan Windowfakta sederhana bahwa itu bukan jendela vanila. Hanya menghindari pengecekan tipe atau mencoba menipu TS juga bukan cara untuk melakukannya.
Rutger Willems
Sayangnya, ini tidak berfungsi pada TS 3.6
Jacob Soderlund
65

Global adalah "jahat" :), saya pikir cara terbaik untuk memiliki portabilitas adalah:

Pertama, Anda mengekspor antarmuka: (mis .: ./custom.window.ts)

export interface CustomWindow extends Window {
    customAttribute: any;
}

Kedua, Anda mengimpor

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

Jendela var global cor ketiga dengan CustomWindow

declare let window: CustomWindow;

Dengan cara ini Anda tidak memiliki garis merah di IDE yang berbeda jika Anda menggunakan dengan atribut objek window yang ada, jadi pada akhirnya coba:

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

Diuji dengan Script 2.4.x dan terbaru!

onalbi
sumber
1
Anda mungkin ingin memperluas mengapa global itu jahat. Dalam kasus kami, kami menggunakan API yang tidak standar pada objek jendela. Itu polyfilled untuk saat ini. Apakah itu benar-benar jahat?
Mathijs Segers
1
@MathijsSegers saya tidak tahu terutama kasus Anda tapi .. tentang global jahat bukan hanya tentang javascript .. ada di setiap bahasa .. beberapa alasannya adalah: - Anda tidak dapat menerapkan pola desain yang baik - Masalah memori dan kinerja (Anda memilikinya di mana-mana, cobalah untuk melampirkan sesuatu ke Objek String dan melihat apa yang terjadi ketika Anda melakukan String baru) - Nama tabrakan menyebabkan efek samping pada kode .. (terutama pada javascript yang memiliki sifat async) Saya dapat melanjutkan tetapi saya pikir Anda dapat gambar istirahat ...
onalbi
1
@onabi, saya benar-benar berbicara tentang fitur browser. Ini tersedia secara global karena merupakan browser. Anda benar-benar akan menyarankan untuk definisi global masih salah? Maksud saya kita bisa mengimplementasikan Ponyfills tetapi ini akan mengasapi browser yang sebenarnya mendukung fitur (misalnya api layar penuh). Itu mengatakan saya tidak percaya bahwa semua orang global adalah orang jahat.
Mathijs Segers
2
@MathijsSegers menurut pendapat saya variabel global harus dihindari untuk tidak digunakan atau dimodifikasi di mana kita bisa .. benar bahwa jendela tersedia karena browser ada tetapi juga benar bahwa ada dalam mutasi sepanjang waktu .. jadi jika misalnya kita mendefinisikan sekarang window.feature = 'Fitur'; dan ini digunakan secara besar-besaran pada kode .. apa yang terjadi jika window.feature ditambahkan oleh browser pada semua kode fitur ini ditimpa .. pokoknya saya memberikan penjelasan bahwa kalimat saya tidak bertentangan dengan Anda .. salam ...
onalbi
43

Bagi mereka yang menggunakan CLI Angular itu mudah:

src / polyfills.ts

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

my-custom-utils.ts

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

Jika Anda menggunakan IntelliJ, Anda juga perlu mengubah pengaturan berikut di IDE sebelum pengambilan polyfill baru Anda:

> File 
> Settings 
> Languages & Frameworks 
> TypeScript 
> check 'Use TypeScript Service'.
Stephen Paul
sumber
1
declare globaladalah triknya, dan jawaban ini tidak
sepenuhnya
31

Saya tidak perlu melakukan ini terlalu sering, satu-satunya kasus yang saya miliki adalah ketika menggunakan Redux Devtools dengan middleware.

Saya hanya melakukannya:

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

Atau Anda bisa melakukannya:

let myWindow = window as any;

lalu myWindow.myProp = 'my value';

Dimitar Nikovski
sumber
1
Dalam hal ini Anda juga dapat menginstal paket npm, yang berisi semua definisi - sebagaimana ditentukan dalam dokumen
bbrinx
Anda dapat melakukan ini, tetapi itu bukan jawaban untuk pertanyaan itu, melainkan solusi
Vitalij
Berkat ini, saya dapat mengaktifkan alat redux dengan baik di bawah naskah dengan menggunakan (window as any) .__ REDUX_DEVTOOLS_EXTENSION__ && (window as any) .__ REDUX_DEVTOOLS_EXTENSION __ ())
danivicario
20

Sebagian besar jawaban lain tidak sempurna.

  • Beberapa dari mereka hanya menekan inferensi tipe untuk toko.
  • Beberapa yang lain hanya peduli tentang variabel global sebagai namespace, tetapi tidak sebagai antarmuka / kelas

Saya juga mengalami masalah serupa pagi ini. Saya mencoba begitu banyak "solusi" pada SO, tetapi tidak satupun dari mereka yang tidak menghasilkan kesalahan tipe sama sekali dan memungkinkan pemicu jenis jumping di IDE (webstorm atau vscode).

Akhirnya dari sini

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

, Saya menemukan solusi yang masuk akal untuk melampirkan mengetik untuk variabel global yang bertindak sebagai antarmuka / kelas dan namespace keduanya .

Contohnya di bawah ini:

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

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

Sekarang, kode di atas menggabungkan mengetik namespace MyNamespacedan antarmuka MyNamespaceke variabel global myNamespace(properti jendela).

e-cloud
sumber
2
Terima kasih - ini adalah satu-satunya yang tampaknya dapat Anda gunakan dalam konteks non-modul ambien.
Tyler Sebastian
13

Inilah cara melakukannya, jika Anda menggunakan TypeScript Definition Manager !

npm install typings --global

Buat typings/custom/window.d.ts:

interface Window {
  MyNamespace: any;
}

declare var window: Window;

Pasang pengetikan khusus Anda:

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

Selesai, gunakan‌ ! Naskah tidak akan mengeluh lagi:

window.MyNamespace = window.MyNamespace || {};
Nik Sumeiko
sumber
1
FWIW typingsdibuat usang dengan Typescript 2.0 (pertengahan 2016), dan telah diarsipkan oleh pemiliknya.
mrm
13

Typscript tidak melakukan typecheck pada properti string.

window["newProperty"] = customObj;

Idealnya, skenario variabel global harus dihindari. Saya menggunakannya kadang-kadang untuk men-debug objek di konsol browser.

Irshad
sumber
8

Jika Anda menggunakan Typecript 3.x, Anda mungkin bisa menghilangkan declare globalbagian dalam jawaban lain dan alih-alih hanya menggunakan:

interface Window {
  someValue: string
  another: boolean
}

Ini bekerja dengan saya ketika menggunakan Typecript 3.3, WebPack dan TSLint.

Dana Woodman
sumber
Tidak bekerja di ^3.4.3. Jawaban ini berhasil untuk saya stackoverflow.com/a/42841166/1114926
Hijau
7

Untuk referensi (ini adalah jawaban yang benar):

Di dalam .d.tsfile definisi

type MyGlobalFunctionType = (name: string) => void

Jika Anda bekerja di browser, Anda menambahkan anggota ke konteks jendela browser dengan membuka kembali antarmuka Window:

interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

Gagasan yang sama untuk NodeJS:

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

Sekarang Anda mendeklarasikan variabel root (yang sebenarnya akan hidup di jendela atau global)

declare const myGlobalFunction: MyGlobalFunctionType;

Kemudian dalam .tsfile biasa , tetapi diimpor sebagai efek samping, Anda benar-benar menerapkannya:

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

Dan akhirnya menggunakannya di tempat lain dalam basis kode, dengan:

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

myGlobalFunction("Kevin");
Benoit B.
sumber
Terima kasih, ini adalah contoh terbaik yang saya temukan, dan berfungsi dengan baik.
Nick G.
6

Buat antarmuka khusus memperluas Window dan menambahkan properti khusus Anda sebagai opsional.

Kemudian, biarkan customWindow yang menggunakan antarmuka kustom, tetapi dihargai dengan jendela asli.

Ini bekerja dengan [email protected].

interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

customWindow.MyNamespace = customWindow.MyNamespace {} 
shuizhongyuemin
sumber
5

Bagi mereka yang ingin mengatur properti yang dihitung atau dinamis pada windowobjek, Anda akan menemukan bahwa tidak mungkin dengan declare globalmetode ini. Untuk memperjelas kasus penggunaan ini

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

Anda mungkin mencoba melakukan sesuatu seperti ini

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

Namun di atas akan kesalahan. Ini karena dalam naskah, antarmuka tidak bermain dengan baik dengan sifat yang dikomputasi dan akan melempar kesalahan seperti

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

Untuk menyiasati hal ini, Anda dapat pergi dengan menyarankan casting windowuntuk <any>sehingga Anda dapat melakukan

(window as any)[DynamicObject.key]
agustus
sumber
3
Ini sangat 2019.
Qwerty
4

Menggunakan create-react-app v3.3 Saya menemukan cara termudah untuk mencapai ini adalah dengan memperluas Windowjenis di yang dihasilkan secara otomatis react-app-env.d.ts:

interface Window {
    MyNamespace: any;
}
Kris Dover
sumber
3

Pertama, Anda perlu mendeklarasikan objek jendela dalam lingkup saat ini.
Karena naskah ingin mengetahui jenis objek.
Karena objek jendela didefinisikan di tempat lain, Anda tidak dapat mendefinisikannya kembali.
Tetapi Anda dapat mendeklarasikannya sebagai berikut: -

declare var window: any;

Ini tidak akan mendefinisikan ulang objek jendela atau tidak akan membuat variabel lain dengan nama window.
Ini berarti jendela didefinisikan di tempat lain dan Anda hanya merujuknya dalam ruang lingkup saat ini.

Kemudian Anda dapat merujuk ke objek MyNamespace Anda hanya dengan: -

window.MyNamespace

Atau Anda dapat mengatur properti baru pada windowobjek hanya dengan: -

window.MyNamespace = MyObject

Dan sekarang naskah tidak akan mengeluh.

Mav55
sumber
bolehkah saya tahu kasus penggunaan Anda ingin tahu di mana windowdidefinisikan?
Mav55
2

Saya ingin menggunakan ini di perpustakaan Angular (6) hari ini dan butuh beberapa saat untuk menyelesaikannya seperti yang diharapkan.

Agar pustaka saya menggunakan deklarasi, saya harus menggunakan d.tsekstensi untuk file yang menyatakan properti baru dari objek global.

Jadi pada akhirnya, file tersebut berakhir dengan sesuatu seperti:

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

Setelah dibuat, jangan lupa untuk mengeksposnya di blog Anda public_api.ts.

Itu berhasil untuk saya. Semoga ini membantu.

Beladau
sumber