Classes e structs
✅
Transpilados para classes ES6. Uma classe simples tem um módulo próprio seja o que for que ela declare, inclusive uma vazia sobre os padrões de uma interface ou sobre uma base, e todo módulo que a nomeia a importa; uma classe cuja cadeia de bases chega a um atributo ou a um tipo [ServerOnly] não tem, tanto três níveis abaixo quanto um (class Underline : Mark sobre class Mark : Attribute), inclusive uma base de uma biblioteca referenciada. Uma classe de exceção da app é uma classe, com módulo próprio (ver try / catch / finally). A cadeia é lida pelo que cada base deriva e nunca pelo seu nome, então uma classe sobre uma interface com nome de atributo mantém o seu módulo, e um nome é um módulo onde qualquer declaração dele for um. Uma classe, uma classe estática, um record ou uma struct declarados dentro de outro tipo também são um módulo próprio, com o nome dos tipos que os contêm e o seu, unidos por $ (Cart$Item, A$B$C), que nenhum tipo de topo pode ter: toda referência a ele, dentro do dono ou fora, nomeia esse twin, então uma classe aninhada ao lado de uma classe de topo com o seu nome nunca é tomada por ela, e uma dentro de uma classe só do servidor ou de um atributo não tem nenhum, como o dono não tem: código do cliente que a nomeia é recusado (EQ2010), e uma página declarada nela não tem rota, enquanto uma dentro de uma classe de exceção atravessa como essa classe (Desde 0.2.0-preview.61). Um record e uma struct têm twin seja o que for que declarem, a não ser que estejam marcados [ServerOnly], inclusive um record sobre um record server-only (Desde 0.2.0-preview.61).
Interfaces
✅
Usadas na checagem de tipos do TypeScript (apagadas em runtime). Um membro padrão (uma propriedade, um indexador ou um método com corpo; um indexador desde 0.2.0-preview.61) chega ao twin de toda classe que o herda sem declará-lo: convertido do código-fonte da interface, ou, para as interfaces do próprio SDK que uma app implementa (um tema, uma linguagem de código), delegado à cópia do runtime. A interface de qualquer outro assembly compilado não tem nem um nem outro, e a build recusa a classe (EQ1008) (Desde 0.2.0-preview.59).
Enums
✅
Atravessam como o nome em camelCase (MainAlign.Start → 'start'), e um enum [Flags] como o seu número. .ToString(), interpolação e concatenação dão o nome do membro C# ("Start"), o mesmo texto que o servidor imprime, as flags ligadas de uma combinação ("Read, Write"), um valor que nenhum membro nomeia como os seus dígitos, e nada para um enum anulável nulo. ToString("D"), "X", "F" e "G", o formato e o alinhamento de uma interpolação escrevem o que o .NET escreve, Enum.Parse, TryParse, GetName, GetNames, GetValues e IsDefined respondem como os do .NET, e um dicionário com chave de enum atravessa nos dois sentidos, recusando um nome que nenhum membro tem. Desde 0.2.0-preview.15; os formatos, o texto de flags, os estáticos de Enum e as chaves de enum desde 0.2.0-preview.60.
Genéricos
✅
Totalmente suportados (apagados em runtime).
Extension methods
✅
Resolvidos pelo Semantic Model e transpilados para chamadas diretas.
Async / await
✅
Mapeiam para async / await e Promise.
Expressões lambda
✅
Mapeiam para arrow functions () => {}.
Pattern matching
✅
Padrões is, padrões de propriedade, padrões recursivos. Um tipo sem nada ligado (o is int or long, int => …, case int:) testa o tipo, um long como BigInt, um inteiro como número inteiro e um char como uma string de uma unidade de código. Uma constante num padrão, x is Limits.Max inclusive, é testada pelo seu valor: um null casa com qualquer ausência, um decimal e um NaN comparam por valor, e uma constante long é um BigInt, como todo long (Desde 0.2.0-preview.60). Um padrão posicional sobre um tipo cujo Deconstruct o app escreveu, o de uma extensão inclusive, chama-o uma única vez por valor em cada teste, mesmo entre os braços de um switch e as alternativas de um or, como o .NET, e lê as partes que ele devolve, como uma desconstrução faz: lia os membros com o nome dos seus out, que uma parte calculada pelo Deconstruct não é (Desde 0.2.0-preview.61).
Interpolação de string
✅
Mapeia para template literals ${var} . Um formato com texto entre aspas ({date:dd 'de' MMMM}) mantém as aspas, e uma raw string interpolada mantém as chaves ($$$"""{{x}}""" é {{x}}) (Desde 0.2.0-preview.60). Um alinhamento é o número que é, inclusive uma constante pelo nome ou uma expressão de constantes ({name,Widths.Name}, {name,-(Widths.Name + 1)}), num StringBuilder também (Desde 0.2.0-preview.62).
Literais de string e char
✅
Uma string ou um char mantém o valor que o C# lê, seja qual for a grafia: um surrogate isolado ("x\uD83D"), um caractere de controle e os escapes que só o C# tem ('\a', '\e', '\x041', '\U00000041'). Um literal UTF-8 ("ab"u8) não tem gêmeo e falha a build (EQ1004) (Desde 0.2.0-preview.60).
Null-coalescing
✅
?? e ??= mapeados para os equivalentes JS.
Inicializadores de objeto
✅
new Obj { Prop = 1 } sobre um record, uma struct, uma classe comum, uma exceção ou um tipo do vocabulário cujo twin o runtime transpila (os modelos da planilha e dos formulários, CellRef, FieldError, SheetController…), aplicado quando o construtor já voltou, como o C# o aplica (antes da 0.2.0-preview.61, uma classe comum o recebia como um objeto de config no fim, uma exceção o descartava, e um tipo transpilado do vocabulário o entregava a uma posição que o seu twin não tem; um componente o recebe como as suas props): todo inicializador do tipo já rodou, inclusive o do próprio membro, e os valores são lidos depois deles. Um inicializador de coleção aninhado (Items = { 1, 2 }) adiciona à coleção que o membro guarda, pelo Add que a árvore vinculada liga (o de um método de extensão pela sua casa, e o de uma lista, de um conjunto ou de um membro ICollection<T> como uma chamada a ele é); um inicializador de objeto aninhado (Inner = { N = 3 }) atribui dentro do objeto que o membro guarda; e uma entrada ([key] = value) é escrita pelo indexador do tipo. O par de um dicionário (Map = { { "a", 1 } }) é o seu Add, que recusa uma chave que já está lá. Cada parte é avaliada no método em que foi escrita, então um await num elemento funciona, e cada elemento é aplicado antes de as partes do seguinte serem avaliadas, o membro a que ele adiciona ou atribui lido antes das suas próprias partes, como o C# o aplica. Um elemento sem conversão faz a build falhar (EQ1004) (Desde 0.2.0-preview.61).
field (C# 14)
✅
Propriedades guardadas: o gêmeo ganha um slot $name (um nome que nenhum campo C# pode ter), um getter e o corpo do próprio acessor. Uma estática guarda o slot no tipo, a de um record inclusive, e o slot começa com o inicializador da propriedade ou com o default do seu tipo (Desde 0.2.0-preview.60). A de instância de um record ou de uma struct roda os corpos dos seus acessores sobre o slot, como a de uma classe, e uma cujo getter tem corpo é estado que o record compara e copia; antes da 0.2.0-preview.61 ela era guardada sob o nome da propriedade, e o corpo do setter se perdia (Desde 0.2.0-preview.61).
Propriedades virtuais e override
✅
Uma propriedade que um tipo derivado pode sobrescrever ou que sobrescreve outra (uma auto-propriedade declarada virtual ou override) guarda o valor numa loja própria ($name), sob acessores no protótipo do gêmeo, numa classe, num record e numa struct igualmente, então toda leitura chega ao override e nenhum inicializador chega ao acessor de outro nível: uma auto-propriedade sobre uma calculada, ou uma calculada sobre uma auto-propriedade, lançava no new ou respondia o valor da base antes da 0.2.0-preview.61. Um override que declara só o getter de uma propriedade cuja base tem os dois mantém o setter da base, como o C# o herda. Um record compara, combina no hash e copia a loja, como o .NET compara um campo de apoio. Um gêmeo com loja a escreve no JSON sob o nome da propriedade, então uma server action recebe a propriedade pelo nome C#. Uma diferença fica: num tipo que sobrescreve uma auto-propriedade com outra, base.Name lê o valor do override, onde o .NET lê o campo da própria base, já que as duas compartilham uma loja (Desde 0.2.0-preview.61).
Membros que ocultam (new)
✅
Um método que oculta um membro herdado, com new ou com a mesma assinatura e sem override, é alcançado como o C# o alcança: pelo tipo base responde o método da base, e pelo tipo que oculta, o que oculta, numa chamada, num method group, numa chamada base e num ?. igualmente, um método genérico sobre o seu próprio parâmetro de tipo incluído, e o mesmo vale para um membro que o runtime lê pelo seu nome .NET, então ((object)c).GetHashCode() é o de object sobre um public new int GetHashCode(). O gêmeo o guarda sob um nome próprio (name$1), e um override de um método new virtual ocupa o lugar desse método. O mesmo vale para um método ao lado de uma implementação explícita de interface do seu nome, então uma chamada pela classe chega a ele e uma pela interface à explícita. Um que oculta um membro, ou fica ao lado de uma implementação assim, e responde a uma interface falha o build (EQ1007), e uma propriedade ou um campo que oculta outro ainda divide o nome com ele (#563, desde 0.2.0-preview.61). Acessores init
✅
Emitidos como setter JS, porque um corpo de init é onde um tipo declara a invariante dele, e descartá-lo perderia a checagem no cliente.
Inicializadores de coleção
✅
new List<int> { 1, 2 } mapeia para [1, 2]. Uma lista construída com um argumento e um inicializador guarda os dois, como o C# a constrói: uma cópia da fonte, nunca a própria fonte, ou nada para uma capacidade, e depois cada elemento em ordem, a fonte ou a capacidade lida primeiro. Uma capacidade ainda é avaliada, e uma negativa é recusada como o construtor a recusa. Assim new List<int>(source) { 3 } é [...source, 3] e new List<int>(10) { 1, 2 } é [1, 2], escrita por extenso ou com o tipo do alvo, e List<int> c = new(source) é uma cópia. Uma capacidade descartava os elementos, e uma fonte era juntada a eles por uma vírgula, antes da 0.2.0-preview.61 (Desde 0.2.0-preview.61).
IEnumerable<T> próprio
✅
Uma classe, um record ou uma struct que implementa IEnumerable<T>, ou só IEnumerable, é enumerada como o seu próprio GetEnumerator() diz, por foreach, um spread, string.Join e todo operador LINQ, cada um pelo que o C# liga para ele: um foreach sobre a classe percorre o seu próprio GetEnumerator() público (o de um enumerador struct incluído), e LINQ, um spread, string.Join e um foreach sobre a interface percorrem o de IEnumerable<T>, explícito ou não, então os dois podem percorrer sequências diferentes, como no .NET. É a sequência que um método iterador produz, ou um enumerador seu, uma classe que é o próprio enumerador incluída, percorrido pelo seu MoveNext e Current e descartado quando o percurso acaba, inclusive por um break. O override de uma classe derivada responde, o IEnumerable.GetEnumerator() explícito ao lado do genérico é a mesma sequência, e também um Current explícito ao lado de um público. Uma classe só com um GetEnumerator() público também é percorrida pelo foreach. O gêmeo tem uma iteração só e um Current só, então um tipo que implementa IEnumerable<T>, ou IEnumerator<T>, sobre dois tipos falha o build (EQ1007), e também um IEnumerable.GetEnumerator() ou um Current explícito que responde de outro modo que o membro que o gêmeo mantém, onde um que devolve a resposta dele (=> GetEnumerator();, => Current;) é guardado como um só. O GetEnumerator() de uma coleção do .NET (=> items.GetEnumerator()) não tem tradução e falha o build (EQ2004): produza os itens com yield (#612, desde 0.2.0-preview.61). Desconstrução
✅
var (a, b) = tuple mapeia para [a, b] = tuple, e também (var c, var d) = … e designações aninhadas. Um record ou um struct se desconstrói pelo Deconstruct que chama, em toda forma: uma declaração, uma atribuição ((a, b) = point, (var a, b) = point), um foreach (var (a, b) in points) e uma desconstrução aninhada. O de um record lê os seus membros, e um Deconstruct escrito pela app é executado. Os receptores e os índices dos alvos são avaliados antes do valor, como o C# os avalia, então (xs[i++], xs[i++]) = point escreve os elementos que o C# escreve (Desde 0.2.0-preview.60). Cada parte chega ao seu alvo como o C# a põe lá: convertida para o tipo do alvo (uma parte int num long), e escrita pelo que o alvo é, a entrada de um dicionário pela sua classe, numa atribuição, numa declaração e num laço (Desde 0.2.0-preview.60).
Variáveis de expressão
✅
O que um padrão, um out var ou uma desconstrução declara é declarado onde o C# declara, em todo tipo de membro: no bloco que os contém depois de um if ou de uma instrução de expressão, dentro de um laço ou de um using, e novo a cada iteração e a cada chamada, então um closure guarda o próprio valor. Um nome que o JavaScript reserva (@class) é renomeado uma vez, na declaração e em toda referência (Desde 0.2.0-preview.59).
Funções locais
✅
Transpiladas para funções internas. Um parâmetro out ou ref leva o seu valor de volta a quem chama, como o de um método (Desde 0.2.0-preview.60).
Laços for
✅
1:1. A variável de um laço é uma por laço, como no C#: um closure criado no corpo lê o valor atual dela, nunca uma cópia da própria iteração, e o mesmo vale para o que o inicializador declara (out var n, var (i, j) = (0, 3)), um cabeçalho de expressões incluído (for (Seed(out var n); …)) (Desde 0.2.0-preview.60).
Delegates
✅
Chamados como valores, pelo nome, por um membro ou a partir de qualquer expressão (handlers[0](x), Make()(x), desde 0.2.0-preview.60).
Records (with)
✅
Uma cópia do record na sua própria classe, e depois os membros que a expressão nomeia: como a cópia do .NET, não roda inicializador nem construtor. O with de uma struct, e o dos records do motor de código, passam pela cópia do próprio twin. Um record cuja cadeia declara um construtor de cópia é copiado por ele, como o with do C#: um declarado começa os seus membros no zero, roda a cópia da base com o que o : base(…) passa e depois o seu corpo, e um nível sintetizado copia os próprios membros (Desde 0.2.0-preview.61).
Construtores
✅
O construtor do twin de um record, de uma struct ou de uma classe comum é o construtor C# que a chamada liga, cada um alcançado pelas quantidades de argumentos que aceita, contando os parâmetros opcionais (antes da 0.2.0-preview.61, uma classe comum ficava só com o mais largo). Um que faz o próprio trabalho (o primário, ou um explícito que não encadeia) liga os seus parâmetros, roda todo inicializador na ordem da declaração, o de um campo e o de uma propriedade igualmente, os de um tipo derivado antes do construtor da base, chama o construtor da base com os seus próprios argumentos e roda o seu corpo; um que encadeia com : this(…) roda aquele em que encadeia e depois o próprio corpo, mesmo quando o corpo daquele retorna cedo. Cada argumento cai no lugar do seu parâmetro e é avaliado na ordem em que está escrito, em new, : this(…), : base(…) e numa cláusula de base: um nomeado, um opcional omitido, e um array params passado inteiro (nomeado ou não) ou formado pelos seus elementos; uma variável que um argumento declara (out var n) é declarada onde ele é avaliado, e o parâmetro de um construtor é uma variável que o seu corpo pode atribuir. O construtor de cópia de um record não é um ramo. Dois construtores que aceitam a mesma quantidade de argumentos fazem a build falhar (EQ1009), e também um encadeamento num construtor que encadeia por sua vez e um : this() para o construtor implícito de uma struct ao lado de construtores próprios. O zero de uma struct (default, a posição de um array, new S() sem construtor próprio, inclusive o de uma struct genérica, que guarda o zero de cada argumento de tipo, e o de uma de outro assembly) não roda nada disso, nem o construtor estático. O construtor primário de uma classe comum liga os seus parâmetros como o de um record, e um que um membro lê fica guardado em cada instância com o seu nome, então um que cai no nome de outro membro faz a build falhar (EQ1007). Uma diferença fica: o construtor de uma base que lê um membro de uma classe derivada por um membro virtual o lê sem valor, onde o .NET o lê inicializado, porque o JavaScript não deixa nada tocar a instância antes de o super() voltar (Desde 0.2.0-preview.61).
Indexadores
✅
Um indexador de instância é o item(…) e o setItem(…, value) do seu twin, e todo acesso a ele os chama: uma leitura, uma escrita, uma atribuição composta, ++ num valor de qualquer tipo, ??=, ?[…], o alvo de uma desconstrução, uma chave a partir do fim (^1, na contagem que o seu tipo nomeia) e a entrada de um inicializador de objeto, com o receptor e as chaves avaliados uma vez cada, cada chave no lugar do seu parâmetro (uma chave nomeada, uma opcional omitida, uma params). Uma atribuição responde o valor que atribuiu, seja o que for que o setter faça com a sua cópia, e uma implementação explícita do indexador de uma interface responde aos acessos pela interface. Um segundo indexador num tipo, ou qualquer membro em Item ou SetItem ao lado de um, faz a build falhar (EQ1007) (Desde 0.2.0-preview.61). Um acesso por uma interface de lista (IList<T>, IReadOnlyList<T>, IList) alcança o que a interface guarda quando roda, o elemento de um array ou de uma lista e o item e o setItem de um twin igualmente, em cada uma dessas formas, e o seu Count conta um tipo seu pelo próprio Count, nunca por um Length ao lado dele: a interface lia um subscrito e um length, que um twin não responde, antes da 0.2.0-preview.61 (Desde 0.2.0-preview.61).
Intervalos
✅
x[a..b] sobre uma string, um array ou uma lista é o slice do JavaScript, e um extremo que pode ser ^0 se resolve contra o comprimento como o Index.GetOffset faz. Sobre um tipo seu com um Length (ou um Count) e um Slice(int start, int length), chama esse Slice como o C# rebaixa o intervalo: o receptor uma vez, depois os extremos na ordem em que estão escritos, depois a contagem, lida só onde um extremo conta a partir do fim ou o fim fica aberto. Assim strip[1..3] é Slice(1, 2), onde chamava o slice(1, 3) do twin antes da 0.2.0-preview.61. Um intervalo passado a um indexador que recebe o próprio Range faz a build falhar (EQ2004), como um Range guardado como valor faz (Desde 0.2.0-preview.61).
Inicialização estática
✅
Os estáticos de um tipo começam no seu zero, depois os inicializadores rodam na ordem da declaração, depois o construtor estático, uma vez, na primeira vez que um deles é lido ou escrito: static int A = B + 1; static int B = 2; deixa A em 1, como no .NET, num record, numa struct, numa classe, numa classe estática e num componente. Um tipo com construtor estático o roda antes da sua primeira instância e do primeiro uso de qualquer um dos seus membros estáticos, inclusive um método, uma propriedade calculada e um operador. Os inicializadores rodam na ordem da declaração entre campos, propriedades e eventos de campo, e o corpo do construtor estático depois deles, com um return nele terminando só ele; um que lança faz todo uso do tipo lançar uma TypeInitializationException, como o .NET faz; um estático cujo inicializador o C# reduz a uma constante é o seu valor; e o construtor estático de um componente roda uma vez (Desde 0.2.0-preview.61).
typeof
✅
Mapeia para o literal de string com o nome do tipo.
Tipos base
✅
Uma classe estende o gêmeo da base como quer que a base esteja escrita: sem qualificação, com o namespace, por um alias ou com global:: (Desde 0.2.0-preview.60).
Nomes de tipo qualificados
✅
Um tipo alcançado pelo seu namespace, por parte dele, por ele inteiro ou por global:: é importado como um tipo alcançado por um using: dentro de App.Chat, Portal.Fold.Text(n) responde o que Fold.Text(n) responde com using App.Portal; (Desde 0.2.0-preview.61).
Chamadas base
✅
Mapeiam para a palavra-chave super.
using static
✅
Um membro alcançado pelo nome simples responde o que a forma qualificada responde: PI é Math.PI, Join(…) é string.Join(…), o High de um enum é Level.High, e Now sob using static System.DateTime é DateTime.Now (Desde 0.2.0-preview.61; até então um membro do .NET fora de Math, MathF, string, uma constante e um enum falhava o build mesmo onde a forma qualificada tinha tradução). Um membro do .NET cuja forma qualificada também não tem tradução falha o build (EQ2004) em vez de chegar ao browser (Desde 0.2.0-preview.60).
O Count de um tipo
✅
Um tipo que a app ou uma biblioteca que ela referencia declara lê o próprio Count: o modelo de domínio de uma biblioteca o lia como o length de um array, undefined (Desde 0.2.0-preview.60).
cast e as
✅
Mapeiam para passagem/truncamento em JS.
sizeof
✅
Mapeia para os tamanhos dos primitivos C#.
Métodos anônimos
✅
delegate(...) { ... } mapeia para arrow functions.
params
✅
Um parâmetro REST (...xs). As duas formas de chamada do C# funcionam: argumentos expandidos e um array passado inteiro, que é espalhado. Desde 0.2.0-preview.15.
stackalloc
✅
Mapeia para typed arrays (ex.: Int32Array).
yield return
✅
Mapeia para generator functions do JS (function*).
try / catch / finally
✅
As cláusulas catch são UM catch JavaScript que as tenta na ordem do C#: cada uma pelo seu tipo de exceção, um tipo derivado dele incluído, e pelo seu filtro when, que declara as próprias variáveis (out var n, um padrão) e responde false onde lança, como o do .NET. Uma exceção que nenhuma cláusula pega segue adiante, e throw; relança a exceção capturada. Uma exceção é um Error JavaScript que carrega os seus tipos .NET, uma classe de exceção da própria app e Exception e = new("…") incluídas, então e is ArgumentException, um switch sobre exceções e as também testam o tipo, e uma exceção que o runtime lança é do tipo que o .NET lança ali (um estouro, uma chave ausente, um formato inválido, um cast recusado); um TypeError do JavaScript é uma NullReferenceException. Uma diferença fica, a da plataforma: o .NET roda um filtro ANTES dos blocos finally entre o throw e a cláusula, e o JavaScript depois deles. Desde 0.2.0-preview.60. A mensagem de uma exceção do framework é composta como o .NET a compõe: quando nenhuma mensagem ou uma nula foi dada, o texto que o .NET escreve para aquele construtor, lido do próprio .NET, depois (Parameter 'x'), e o valor real e o nome de um objeto descartado em linhas próprias, as mensagens internas de uma agregada depois da sua e a frase de um inicializador de tipo, e ParamName, ActualValue, InnerException e TypeName leem o que o construtor recebeu, o InnerException de uma agregada sendo a primeira interna, as exceções de argumento do próprio runtime nomeando o seu parâmetro também (Desde 0.2.0-preview.61). Um argumento nulo é recusado como o .NET o recusa, uma ArgumentNullException que nomeia o parâmetro, pelos Max, Min e ToDictionary do LINQ, por um operador LINQ que recebe uma sequência que não é uma lista (o second do Concat, o first do Zip), por new Guid(text), char.GetUnicodeCategory(text, i), CancellationToken.Register e CreateLinkedTokenSource, e CompareTo(object) responde 1 para um nulo, como o do .NET. Um membro traduzido para o método do próprio JavaScript, o LINQ sobre uma lista entre eles, ainda lê um nulo através do nulo, uma NullReferenceException: a suíte de conformidade chama cada membro da superfície traduzida com um nulo em cada parâmetro de referência, e lista esses em null-argument-gaps.baseline.txt com as suas razões (Desde 0.2.0-preview.61). Uma classe de exceção da app é uma classe sobre a base de exceção do runtime, o Error que carrega os seus tipos: os seus campos, as suas propriedades, os seus construtores e os seus métodos são dela, e a sua chamada à base entrega o que um new da sua base do .NET entrega, então : base(message, inner) chega a Message e a InnerException, : base("bad", name) sobre ArgumentException lê bad (Parameter 'x') e o seu ParamName, uma classe sobre InvalidOperationException que não dá mensagem lê o texto próprio daquele tipo, uma mensagem ausente sobre Exception é o padrão do .NET (Exception of type 'App.Failure' was thrown.), um membro dela sob um nome que a base guarda (um Name, um override de Message ou de ParamName) responde por ele, e uma genérica é capturada pela sua construção (catch (Failed<int>)) desde que o seu construtor roda, um throw this ali incluído. Uma mensagem ausente nomeia o tipo como o .NET o nomeia em execução (App.Failed1[System.Int32], App.Outer+Inner). Ela é transpilada como qualquer classe: uma que o browser nunca vê, cujos membros chegam ao que ele não consegue rodar, é marcada [ServerOnly], e dois construtores dela que aceitam a mesma quantidade de argumentos são recusados (EQ1009), menos o que só a serialização do .NET chama, (SerializationInfo, StreamingContext), que o template do Visual Studio escreve e o gêmeo deixa de fora, e uma chamada desse que o browser faria, um new ou a chamada à base de outro construtor, falha o build (EQ1009). Uma exceção construída ou testada onde um argumento de tipo que ela carrega é um parâmetro de tipo também falha o build (EQ2014), já que o JavaScript o apaga: new Failed<T>() num método genérico, catch (Failed<T>), new Missing() dentro da classe genérica que a declara. Uma declarada dentro de outro tipo também é uma classe, com o gêmeo nomeado pelo dono (Holder$Nested), e o que uma classe de exceção declara também, um record do motivo que ela carrega (Rejected$Reason). Num trecho compilado sozinho, sem projeto de onde ler as suas bases, uma classe de exceção da app ainda é o Error` do runtime com os seus tipos e nenhum dos seus membros (#611, desde 0.2.0-preview.61). Expressões throw
✅
x ?? throw …, c ? a : throw … e => throw …, a exceção avaliada só onde é lançada, um await nela incluído. Desde 0.2.0-preview.60.
lock
⚠️
Transpilado só para o corpo (JS é single-threaded). A expressão roda uma vez, na frente dele, a menos que só leia uma variável, um campo ou this (Desde 0.2.0-preview.60; a que declara uma variável, desde 0.2.0-preview.59). new object(), o portão de costume, é um objeto novo, com ou sem um inicializador vazio.