Sería útil cuando un módulo puede implementar una interfaz usando la palabra clave implements
. Sintaxis: module MyModule implements MyInterface { ... }
.
Ejemplo:
interface Showable {
show(): void;
}
function addShowable(showable: Showable) {
}
// This works:
module Login {
export function show() {
document.getElementById('login').style.display = 'block';
}
}
addShowable(Login);
// This doesn't work (yet?)
module Menu implements Showable {
export function show() {
document.getElementById('menu').style.display = 'block';
}
}
addShowable(Menu);
¿Cómo funcionaría esto con módulos externos? Es probable que una vez que las personas puedan usarlo para uso interno, también quieran usarlo con externo.
Buena pregunta. No sé qué sintaxis sería la mejor, pero aquí hay algunas sugerencias:
implements Showable; // I would prefer this one.
module implements Showable;
export implements Showable;
Solo debería permitirse en módulos externos que no usen una asignación de exportación, ya que si usa una asignación de exportación, lo que exporta ya puede tener un implements
en otro lugar.
Aprobado. Preferimos la sintaxis
export implements Showable;
y acordó que esto es innecesario para las asignaciones de archivos export =
.
Algunas preguntas más:
declare module "Module" implements Interface { }
import i : Interface = require("Module");
module Foo {
export interface IBar {
(a:string): void;
}
export module Bar implements IBar { // should this be an error?
export interface Interface {}
}
function Bar(a: string) : void { } // not exported
}
var bar: Foo.IBar = Foo.Bar;
Debe permitirse en módulos externos ambientales. Para estos módulos, en mi opinión, deberían permitirse dos sintaxis:
declare module "first" implements Foo { }
declare module "second" {
interface Bar { }
export implements Bar; // this syntax is necessary, with the first syntax you can't reference Bar.
}
¿O debería Bar estar en el alcance de una cláusula de implementos antes de la apertura {
?
Agregar información de tipo a una declaración de importación no es realmente útil en mi opinión, ya que puede agregar la información de tipo al módulo en sí.
Y para las declaraciones fusionadas, diría que el bloque de módulo que contiene la cláusula implements debería implementar la interfaz. Eso también evita problemas de visibilidad.
¿Cómo se relacionaría esto con el número 2159? ¿Un espacio de nombres implementa una interfaz?
@jbondc Si tuviéramos esto, también se aplicaría a los espacios de nombres. Debe pensar en los módulos internos y los espacios de nombres como isomórficos.
¿Está seguro de que desea seguir una ruta de implementación donde los "espacios de nombres" puedan implementar interfaces?
Oh, vaya, esto ha sido aprobado por bastante tiempo. @RyanCavanaugh, @DanielRosenwasser, @mhegazy a menos que tenga ningún tipo de dudas o retoques, probablemente implementar esta soonish.
Retiro mi escepticismo anterior, de hecho salí por las nuevas posibilidades estructurales que traería.
De acuerdo con eso, considere aplicar la interfaz del agregado de la interfaz en lugar de solo el bloque que declara la implementación: la naturaleza de los espacios de nombres / módulos debe extenderse y contener muchos componentes no triviales. Me gustaría poder usar esto, pero ciertamente no quiero definir todo mi espacio de nombres / módulo en el mismo archivo. ¿Por qué no usar una clase en ese caso?
@ Elephant-Vessel No estoy seguro de si estamos hablando de módulos, espacios de nombres, paquetes, características o ...
@aluanhaddad ¿Qué quieres decir?
Quiero decir que en el momento en que comenzó esta discusión, el módulo no significaba lo que significa hoy. Ahora usamos el término espacio de nombres para referirnos a lo que se describe en el OP como módulo, mientras que módulo ha adquirido un significado más preciso e incompatible. Entonces, cuando habla de varios archivos que participan en esta implementación, ¿se refiere a espacios de nombres o módulos?
Me refiero a los espacios de nombres. Supongo que solo quería ajustarme a la historia de este hilo, lo siento por no soltarme :) O cuando lo pienso, tal vez tenía el término genérico 'módulo' en mi cabeza, que describe una unidad de nivel superior que consiste en un conjunto de subcomponentes, ensamblados para proporcionar cierta funcionalidad de alto nivel en un sistema. Pero estoy bien con solo ir con 'espacios de nombres'.
Así que quiero poder describir y poner restricciones y expectativas en [_módulos genéricos_] que pueden contener otros [_módulos genéricos_] o clases, aprovechando los espacios de
Mi esperanza es que seamos capaces de expresar mejor las expectativas estructurales de mayor nivel en un sistema. Las clases no escalan bien, están bien como componentes atómicos en un sistema, pero no creo que la estructura organizacional de nivel superior en un sistema sea buena para expresarse con clases, ya que están diseñadas para ser instanciadas y heredadas y cosas así. . Es demasiado hinchado.
Apreciaría una forma simple y limpia de describir la estructura de orden superior del sistema, sin problemas. Preferiblemente con el único problema son las restricciones de visibilidad direccional opcionales. Como hacer que sea imposible hacer referencia a _MySystem.ClientApplication_ de _MySystem.Infrastructure_ pero está bien al revés. Entonces empezaríamos a ir a algún lugar emocionante.
@ Elephant-Vessel gracias por aclarar. Estoy de acuerdo en que esto sería extremadamente valioso y que los tipos de clases no son el enfoque correcto aquí. Creo que ha dado en el clavo cuando se habla de creación de instancias porque los espacios de nombres representan cosas que son conceptualmente únicas a nivel de biblioteca. Aunque esto no se puede hacer cumplir, sería útil conceptualmente tener algo que no _implique_ múltiples instancias.
Estoy de acuerdo con @ Elephant-Vessel. Si bien es fácil confundir TypeScript con otro Java, donde todas las restricciones se expresan con una estructura de clase única, TS tiene un concepto de "Forma" mucho más amplio que es muy poderoso y elimina el contortonismo semántico. Desafortunadamente, la incapacidad de poner restricciones en el módulo tiende a obligar a los desarrolladores a volver a un patrón de clase para cosas que se expresarían mucho mejor como módulo.
Por ejemplo, para las pruebas unitarias, sería muy útil poder expresar alguna "forma" (es decir, restricciones) en los módulos para que podamos proporcionar una implementación alternativa para un contexto de ejecución particular. Ahora, parece que la única forma de hacerlo de una manera estructurada / verificada es volver a la DI basada en clases (como la Spring) y convertir todo en una clase (y por lo tanto instanciable).
De todos modos, estoy parafraseando a @ Elephant-Vessel, pero si tengo un solo deseo de TS, sería este.
¿Alguna noticia sobre este pájaro? Yo también tengo este problema
así que, uhh, ¿no sería un simple caso de:
export {} as IFooBar;
¿Qué pasa con esa sintaxis? Supongo que la sintaxis ya ha sido aprobada, tal vez como
export implements IFooBar
de todos modos deseando que llegue
¿Ya se ha matriculado / aterrizado? esta va a ser una característica interesante
¿Cómo podemos progresar en esto? Es increíblemente poderoso. ¡Feliz de ayudar!
¿Algún trabajo sobre este birb? Una pregunta que tengo por el momento es cómo puedo declarar una interfaz para la exportación predeterminada. Por ejemplo:
export default {}
Supongo que puedo hacer:
const x: MyInterface = {}
export default x;
eso funcionaría para la mayoría de los archivos TS, aunque el problema es que si está codificando para JS primero y planea hacer la transición a TS más tarde, entonces esto no funciona tan bien.
Otra cosa en la que estaba pensando, ¿qué pasa con los espacios de nombres que implementan? Algo como:
export namespace Foo implements Bar {
}
Supongo que Bar sería un espacio de nombres _abstract_ lol idk
He visto esta pregunta surgir tantas veces, y creo que todos solo estamos buscando una cosa:
Admite miembros estáticos en una interfaz.
Si eso sucediera, podría usar una clase con miembros estáticos y una interfaz, que es casi lo mismo que está tratando de hacer aquí, ¿verdad?
De cualquier manera, es muy necesario agregar soporte estático a las interfaces O agregar soporte de interfaz para módulos.
@shiapetel nah no es así.
Podemos hacer esto:
export default <T>{
foo: Foo,
bar: Bar
}
pero eso no es lo que estamos buscando. buscamos específicamente:
export const foo : Foo = {};
export const bar : Bar = {};
pero actualmente no hay ningún mecanismo para hacer cumplir el módulo para exportar foo y bar. Y, de hecho, tampoco existe ningún mecanismo para exigir que el módulo exporte el valor predeterminado correcto.
Los miembros estáticos de interfaces de
¿Este problema solo está esperando a que alguien intente implementarlo?
Un caso de uso sería para marcos y herramientas que escanean un directorio en busca de módulos al iniciar la aplicación, esperando que todos esos módulos exporten una determinada forma.
Por ejemplo, Next.js escanea ./pages/**/*.{ts,tsx}
para los módulos de su página, generando rutas basadas en sus nombres de archivo. Depende de usted asegurarse de que cada módulo exporte las cosas correctas (un NextPage
como exportación predeterminada y un PageConfig
exportación opcional llamado config
):
import { NextPage, PageConfig } from 'next'
interface Props { userAgent?: string }
const Home: NextPage<Props> = ({ userAgent }) => (<main>...</main>)
Page.getInitialProps = async ({ req }) => {
const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
return { userAgent }
}
export default Page
export const config: PageConfig = {
api: { bodyParser: false }
}
Sería bueno si en su lugar pudiera declarar la forma de exportación de todo el módulo en una línea cerca de la parte superior, como implements NextPageModule<Props>
.
Otro pensamiento: sería interesante si hubiera alguna forma de especificar en una configuración de TypeScript que todos los archivos que coincidan con un patrón determinado (como ./pages/**/*.{ts,tsx}
) deben implementar una determinada forma de exportación, por lo que un módulo podría tener su tipo de exportación- comprobado simplemente porque se encuentra dentro del directorio pages
, por ejemplo. Pero no estoy seguro de si hay algún precedente para este enfoque, y podría resultar confuso.
A menudo me siento tentado a crear una clase Singleton cuando todo lo que necesito es un módulo simple que implemente una interfaz. ¿Algún consejo sobre la mejor manera de abordar esto?
@RyanCavanaugh @DanielRosenwasser
Quiero trabajar en este tema. ¿Puede darme algunos consejos para la solución o dónde buscar?
Pensando en esto desde una perspectiva de 2020, me pregunto si en lugar de export implements Showable
reutilizamos type
y permitimos export
como identificador. Hoy en día, esa sintaxis no es
Luego obtenemos la sintaxis de importación:
// Can re-use the import syntax
type export = import("webpack").Config
Las declaraciones son fáciles de escribir:
// Can use normal literals
type export = { test: () => string, description: string }
// Generics are easy
type export = (props: any) => React.SFC<MyCustomModule>
También vale la pena pensar cuál debería ser el equivalente de JSDoc, tal vez:
/** <strong i="17">@typedef</strong> {import ("webpack").Config} export */
Hay algunas notas en ^ - una cosa interesante que surgió de la reunión fue la idea de que podríamos construir una herramienta más genérica de la cual este es un caso de uso, en lugar de lo único que hace.
Por ejemplo, si tuviéramos un operador de aserción de tipos para la compatibilidad de tipos, entonces eso podría usarse tanto para las exportaciones de módulos como, de manera genérica, para verificar que los tipos coinciden como lo desea. Por ejemplo:
type assert is import("webpack").Config
const path = require('path');
export default {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
};
Donde la falta de un objetivo significa aplicarlo en el ámbito de nivel superior. Esto se puede usar para proporcionar escritura contextual (por ejemplo, obtendría autocompletar en el export default { en|
Pero también puede ser útil para validar sus propios tipos:
import {someFunction} from "./example"
type assert ReturnType<typeof someFunction> is string
También vale la pena pensar cuál debería ser el equivalente de JSDoc, tal vez:
js /** <strong i="7">@typedef</strong> {import ("webpack").Config} export */
Creo que @module
sería el equivalente de JSDoc. La parte superior del archivo debe tener:
js
/** <strong i="12">@module</strong> {import("webpack").Config} moduleName */
Storybook v6 ha cambiado a un enfoque basado en módulos estructurados que llamaron Component Story Format . Se espera que todos los módulos .stories.js/ts
en una base de código incluyan una exportación predeterminada con el tipo Meta
.
No tener forma de expresar esta expectativa de una manera global, combinado con la deficiencia existente en la escritura de exportaciones predeterminadas, hace que usar Storybook v6 con TypeScript sea una experiencia mucho menos fluida de lo que podría ser.
Para agregar a los puntos de @jonrimmer , exportar un default
que sea de cierto type
que replica un module
dará lugar a problemas con la vibración de árboles.
Webpack no tiene problemas para sacudir los árboles import * as Foo
. Pero cuando intenta hacer lo mismo con export default const = {}
o export default class ModuleName {
con todos los miembros estáticos, las importaciones no utilizadas no se eliminan.
Comentario más útil
Un caso de uso sería para marcos y herramientas que escanean un directorio en busca de módulos al iniciar la aplicación, esperando que todos esos módulos exporten una determinada forma.
Por ejemplo, Next.js escanea
./pages/**/*.{ts,tsx}
para los módulos de su página, generando rutas basadas en sus nombres de archivo. Depende de usted asegurarse de que cada módulo exporte las cosas correctas (unNextPage
como exportación predeterminada y unPageConfig
exportación opcional llamadoconfig
):Sería bueno si en su lugar pudiera declarar la forma de exportación de todo el módulo en una línea cerca de la parte superior, como
implements NextPageModule<Props>
.Otro pensamiento: sería interesante si hubiera alguna forma de especificar en una configuración de TypeScript que todos los archivos que coincidan con un patrón determinado (como
./pages/**/*.{ts,tsx}
) deben implementar una determinada forma de exportación, por lo que un módulo podría tener su tipo de exportación- comprobado simplemente porque se encuentra dentro del directoriopages
, por ejemplo. Pero no estoy seguro de si hay algún precedente para este enfoque, y podría resultar confuso.